目录
struct和class的区别
final和override关键字
浅拷贝和深拷贝
内联函数和宏定义
new和delete
malloc与free的实现原理
类成员初始化方式?构造函数的执行顺序 ?为什么用成员初始化列表会快一些?
struct | class | |
---|---|---|
相同点 |
|
|
不同点 | struct默认是public继承 | class默认是private继承 |
当不希望某个类被继承,或不希望某个虚函数被重写,可以在类名和虚函数后添加final关键字,添加final关键字后被继承或重写,编译器会报错。例子如下:
class Base
{
virtual void foo();
};
class A : public Base
{
void foo() final; // foo 被override并且是最后一个override,在其子类中不可以重写
};
class B final : A // 指明B是不可以被继承的
{
void foo() override; // Error: 在A中已经被final了
};
class C : B // Error: B is final
{
};
当在父类中使用了虚函数时候,你可能需要在某个子类中对这个虚函数进行重写(override),以下方法都可以:
class A
{
virtual void foo();
}
class B : public A
{
void foo(); //OK
virtual void foo(); // OK
void foo() override; //OK
}
浅拷贝
浅拷贝只是拷贝一个指针,并没有新开辟一个地址,拷贝的指针和原来的指针指向同一块地址,如果原来的指针所指向的资源释放了,那么再释放浅拷贝的指针的资源就会出现错误。
深拷贝
深拷贝不仅拷贝值,还开辟出一块新的空间用来存放新的值,即使原先的对象被析构掉,释放内存了也不会影响到深拷贝得到的值。在自己实现拷贝赋值的时候,如果有指针变量的话是需要自己实现深拷贝的。
示例如下:
#include
#include
using namespace std;
class Student
{
private:
int num;
char *name;
public:
Student(){
name = new char(20);
cout << "Student" << endl;
};
~Student(){
cout << "~Student " << &name << endl;
delete name;
name = NULL;
};
Student(const Student &s){//拷贝构造函数
//浅拷贝,当对象的name和传入对象的name指向相同的地址
name = s.name;
//深拷贝
//name = new char(20);
//memcpy(name, s.name, strlen(s.name));
cout << "copy Student" << endl;
};
};
int main()
{
{// 花括号让s1和s2变成局部对象,方便测试
Student s1;
Student s2(s1);// 复制对象
}
system("pause");
return 0;
}
//浅拷贝执行结果:
//Student
//copy Student
//~Student 0x7fffed0c3ec0
//~Student 0x7fffed0c3ed0
//*** Error in `/tmp/815453382/a.out': double free or corruption (fasttop): 0x0000000001c82c20 ***
//深拷贝执行结果:
//Student
//copy Student
//~Student 0x7fffebca9fb0
//~Student 0x7fffebca9fc0
内联函数:是一种特殊的函数,编译器会尝试将其内联展开,而不是通过函数调用的方式执行。内联函数通常用于执行简单的操作或者频繁调用的函数,以减少函数调用的开销和提高性能。
内联函数适用场景:
内联函数的示例:
#include
// 内联函数示例
inline int addNumbers(int a, int b) {
return a + b;
}
int main() {
int result = addNumbers(5, 3); // 内联函数调用
std::cout << "Result: " << result << std::endl;
return 0;
}
new的实现原理
delete 的实现原理:
初始化方式有两种:
1、赋值初始化,通过在函数体内进行赋值初始化;
class MyClass {
private:
int number;
std::string name;
public:
MyClass() {
number = 0;
name = "Default";
}
void printData() {
std::cout << "Number: " << number << std::endl;
std::cout << "Name: " << name << std::endl;
}
};
2、列表初始化,在冒号后使用初始化列表进行初始化。
class MyClass {
private:
int number;
std::string name;
public:
MyClass(int num, const std::string& n) : number(num), name(n) {
// 构造函数体
}
void printData() {
std::cout << "Number: " << number << std::endl;
std::cout << "Name: " << name << std::endl;
}
};
这两种方式的主要区别在于:
对于在函数体中初始化,是在所有的数据成员被分配内存空间后才进行的。
列表初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),那么分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行。
一个派生类构造函数的执行顺序如下:
① 虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数)。
② 基类的构造函数(多个普通基类也按照继承的顺序执行构造函数)。
③ 类类型的成员对象的构造函数(按照成员对象在类中的定义顺序)
④ 派生类自己的构造函数。
为什么用成员初始化列表会快一些?
赋值初始化是在构造函数当中做赋值的操作,而列表初始化是做纯粹的初始化操作。我们都知道,C++的赋值操作是会产生临时对象的。临时对象的出现会降低程序的效率。
通过构造函数初始化列表,编译器可以生成更高效的代码,避免了临时对象的创建和额外的赋值操作。这种直接初始化的方式可以提高代码的性能,并且在某些情况下,还可以优化构造函数的调用。