C++基础
public和protected和private三种继承有什么区别
三种继承方式对于子类来说没有任何区别,但是对于子类的实例化对象来讲有区别。但是只分为两种,public一种和protected,private一种。后者会使子类的实例化对象无法访问到基类的public成员。
protected和private会影响到子类的子类,即影响到孙子类。如果是protected继承,那么结果和public继承一样,public和protected属性都能够访问,如果是private继承,那么孙子类无法访问到基类的所有属性,因为对他来说这些属性在继承至子类的时候已经变成了private
public继承可以视为是is-a关系,而private继承类似复合(has-a),但是又比复合更第一层
C++的内存结构使怎样的
- 栈区:编译器自动分配释放的区域,局部变量和函数参数就储存在栈区,离开作用域自动释放
- 堆区:程序员手动申请的区域,比如new和malloc
- 全局区:全局变量和静态变量存放的区,程序结束后才释放
- 代码区
New和Malloc的区别
最基本的,new是C++风格的内存申请,它是经过包装的,比malloc更安全。
- 使用new不需要指定需要分配多少空间,而malloc需要。同时new返回的是申请的对象的指针,而malloc返回的是void*,需要使用者自行转换,这是不安全的,而new是类型安全的。如果new分配失败了会抛出bad_alloc异常,而malloc失败了会返回NULL。如果new的是一个对象的话那么他还会调用到它的构造函数
调用new/delete的过程
首先new会调用operator new来分配内存,然后会使用static_cast进行转型,最后利用转型后的指针来调用一波构造函数
operator new是一个可重载的函数,他的内部会调用malloc,若失败了抛出异常,成功了则分配一个指定的内存空间,然后返回一个空指针
delete的话会先执行析构函数,然后再执行operator delete
指针和引用的区别
指针使用的时候可以不需要初始化,引用需要初始化
我们无法控制引用的生命周期,反之我们可以使用delete
来管理指针
引用的本质可以看作是指针常量,因为它引用的目标不能更改,但是可以。所以引用本身的大小和指针一致
指针和引用都能实现多态
static_cast和dynamic_cast的区别
static_cast
可以说是适用于C风格的转换,比如说double
到float
,转换int
到char
等等。但像是char
转int
,子类转父类这种up-cast
,推荐不写cast,直接等于号糊过去
dynamic_cast
用于多态类型的转换,同时它自带IDE检查,只能用于指针和引用的转换
dynamic_cast
自带类型检查,对于指针类型若失败会返回nullptr
,对于引用类型失败会抛出bad_cast
异常,随意使用dynamic_cast
一定要注意检查返回值
两者都在编译器层面都不支持无效的类型转换,且dynamic_cast
在转换失败时会返回一个nullptr
(如果是指针的话),而static_cast
不会,编译时会出错
static_cast和reinterpret_cast的区别
reinterpret_cast
通常用于void*
类型的指针到别的指针的转换
另外的,对某些数据进行reinterpret_cast
能保证其地址内数据不会被改变,假设我需要得到一个内存和float f
一样的int
然后对其进行位操作,那么我们需要这么做
// f的地址中的数据为00 00 80 3f
float f = 1.0f;
// a的地址中的数据为01 00 00 00
int a = static_cast<int>(f);
// b的地址中的数据为00 00 80 3f
// b的值为1065353216
int b = *(reinterpret_cast<int*>(&f));
memcpy和strcpy的区别?
- mencpy能拷贝任意类型的数据,而strcpy只能拷贝字符串
- mencpy需要显示指定拷贝的长度,而strcpy不需要,当他遇到/0的时候会自动停止,这种就可能会导致内存溢出
NULL与nullptr的区别?
C++中的NULL是一个宏,它是(void*)0
,nullptr
是一个关键字,代表空指针。
C++中不允许void*
类型的指针隐式转换为其他类型的指针
nullptr
的类型是nullptr_t
,并不是void*
编译期多态和运行期多态
通俗的来讲这两者的区别就是:应该调用哪一个重载?和 应该绑定哪一个虚函数?
编译期多态是指不同的模板参数具现化导致调用不同的重载函数,STL就是静态多态一个应用的例子,泛型编程。效率较高,但是调试困难
- 函数重载
- 函数模板
运行期多态指利用查虚函数表实现的基类对象调用派生类的成员函数,运行时动态绑定,效率较低
- 虚函数表
虚函数表
如果一个类至少一个虚函数的话,那么这个类就拥有一张虚函数表,如果他有子类,那么子类也会有一张虚函数表。
虚函数表可以看作是一个存放虚函数指针的数组
每个类的实例化对象都会有一个指向该虚函数表的指针,虚函数表的指针是在类的构造函数中初始化的,发生在构造函数函数体之前
函数签名
函数名 函数参数 函数参数顺序 所在的类 所在的名称空间
C语言的函数签名不包含函数参数和参数顺序,所以C语言没有函数重载
static
静态全局变量,静态全局函数,它们都是单文件使用的
静态局部变量,重复声明不会覆盖其值
静态成员变量和静态成员函数:声明周期比类长,每个类有一份,而不是每个类实例,每个类实例共享这一份。类内静态成员函数可以访问其他非静态成员函数和变量,但是类内非静态成员函数不能访问类内静态成员变量和函数
const
修饰变量
修饰指针:顶层指针(指针常量),指向的方向不能更改。底层指针(常量指针),指向的值不能更改
修饰函数参数
修饰函数返回值
修饰函数——常函数
inline
声明和定义都在类内的成员函数默认是inline
内联函数在调用时与宏调用一样展开,不需要函数调用时参数的压栈操作,减少了调用开销
struct和class
默认成员访问权限,public和private;默认继承权限,public和private
class可用于模板中替代typename
其实现版本的C++中struct和class已经几乎一致了,说使用区别的话那应该是为了兼容C语言。struct可以成为一个单纯的数据集合,或者说是POD,而类要担负的就更多,比如做数据处理等等
Union
union中的数据类型是不允许具有构造函数的,可以允许POD的存在。Union中的数据存在于同一片内存中,至于提取出来什么值看你使用什么方法去解释它
union U
{
int a;
char c;
};
int main()
{
U u;
u.c = '1';
cout << u.c << endl; // '1'
u.a = 100;
cout << u.c << endl; // 'd'
}
友元
友元函数
友元全局函数
class A
{
friend void globalVisit();
int data = 100;
int showData() { return data; }
};
void globalVisit()
{
A a;
a.showData();
}
其实这个函数写在类A中也是可以的,但是它同样不能直接访问A对象的private
或protected
对象
void globalVisit()
{
// 不行
cout << data << endl;
}
友元成员函数
首先B中被当成朋友的函数得是public
的,其次该函数需要在类A声明后再实现
class B
{
public:
void visitFriend();
};
class A
{
friend void B::visitFriend();
int data = 100;
int showData() { return data; }
};
void B::visitFriend()
{
A a;
a.showData();
}
友元类
顺序需要是先朋友再主人
class B
{
void visitFriend()
{
A a;
a.showData();
}
};
class A
{
friend B;
int data;
int showData() { return data; }
};
内存对齐
会按照结构中最大的类型作为基准,进行整数倍的对齐
// 大小是16
union U
{
int arr[3];
double d;
};
// 大小是24
struct S1
{
int arr[3];
double d;
};
// 大小是12
struct S2
{
char c1;
int i;
char c2;
};
强制对齐alignas
struct MyStruct
{
alignas(16);
char c;
int i;
}
强枚举类型
普通的枚举类型在同一个namespace中,是不能出现重名的,且能够被隐式转换为int类型的值
而强枚举类型(enum class
)的枚举类型是唯一的,但仍可以显示转换为int,unsigned int等类型
enum class my_enum : unsigned int
{
value1, // 0
value2, // 1
value3 = 100,
value4 = 200,
value5, // 201
};
类的大小
成员函数储存在代码区,不列入计算。静态成员变量储存在全局区,也不列入计算。