正文
1. 构造函数
- 概念和作用
- 构造函数用于在创建对象时对对象进行初始化操作。它确保对象在被使用之前处于一个合理的初始状态。
- 特点
- 无返回值:构造函数不需要返回任何值,它的任务是初始化对象。
- 函数名与类名相同:这是构造函数的一个重要标识,编译器通过这个规则来识别构造函数。
- 必须存在于public中(通常情况):这样才能在类的外部创建对象时调用构造函数。如果构造函数是private的,那么只能在类的内部创建对象,这在一些特殊的设计模式中会用到,但一般情况下我们希望在类的外部能够创建对象,所以构造函数放在public区域。
- 可以重载:可以定义多个构造函数,只要它们的参数列表不同。这样可以根据不同的初始化需求提供多种方式来创建对象。
- 不用于初始化static数据成员:因为static数据成员是属于类的,而不是属于某个具体的对象,它在类加载时就会被初始化,不需要通过构造函数来初始化。
- 默认构造函数(合成的构造函数)
- 自动生成条件:如果在类中没有给出构造函数,编译器会自动生成一个默认的构造函数。这个默认构造函数是无参数的,并且函数体为空。
- 消失条件:当在类中手动给出构造函数时,默认的构造函数就会消失。
- 作用:如果类成员变量有初始值,用这些初始值初始化成员变量;如果类成员变量没有初始值,则默认初始化该成员变量。例如:
class Student {
public:
int age = 18;
long id = 217060;
std::string name;
};
int main()
{
// 执行默认构造函数,age、id、name分别被初始化为18、217060、空字符串
Student stu;
return 0;
}
- default构造函数(C++11标准)
- 设计目的:当我们在类中已经手动给出了构造函数,但仍然需要一个默认构造函数时,可以使用
=default
来定义。 - 语法:
=default
既可以和声明一起出现在类的内部,也可以作为定义出现在类的外部。如果在类的内部,则默认构造函数是内联的;如果它在类的外部,则该成员默认情况下不是内联的。例如:
- 设计目的:当我们在类中已经手动给出了构造函数,但仍然需要一个默认构造函数时,可以使用
class Student {
public:
Student()=default;
Student(int age, int id) {
this->age = age;
this->id = id;
}
public:
int age = 18;
long id = 217060;
std::string name;
};
int main()
{
Student stu; // 相当于执行了“Student()=default”构造函数,也相当于执行了类的默认构造函数
return 0;
}
2. 拷贝构造函数
- 概念和作用
- 拷贝构造函数使用一个已经存在的对象去构造(初始化)另一个对象。它在很多情况下会被隐式地调用,比如当我们用一个对象去初始化另一个对象时。
- 特点
- 参数加上const&:参数通常为
const
引用类型(const&
)。const
是为了防止函数内部修改被拷贝对象的值,&
是为了避免无限循环拷贝(如果不是引用类型,在拷贝过程中可能会再次调用拷贝构造函数,导致无限循环)。
- 参数加上const&:参数通常为
- 分类
- 浅拷贝
- 适用情况:当成员变量无动态内存(指针等)变量时,在拷贝构造函数内对成员变量只做简单的赋值,不做内存申请。例如:
- 浅拷贝
class Cperson
{
private:
int a;
public:
Cperson(Cperson const& other);//拷贝构造
};
Cperson::Cperson(Cperson const& other)
{
this->a = other.a;
}
【深拷贝】
适用情况:当成员变量有动态内存(指针等)变量时,在拷贝构造函数内对成员变量先进行内存申请,然后进行内容拷贝。例如:
class Cperson
{
private:
int age;
char* name;
public:
Cperson(Cperson const& other);
};
Cperson::Cperson(Cperson const& other)
{
this->age = other->age;
if (other.name)//深拷贝
{
int len = strlen(other.name);
this->name = new char[len + 1];
strcpy(this->name, other.name);
}
else
this->name = NULL;
}
int main()
{
Cperson person1;
Cperson person2(person1);//隐式调用拷贝构造
Cperson person2 = person1;//显示调用拷贝构造
}
- 默认拷贝构造函数(合成拷贝构造函数)
- 规则:如果没有主动给出拷贝构造函数,编译器会自动添加一个拷贝构造函数(做的是浅拷贝)。当主动给出拷贝构造函数后,默认的拷贝构造函数消失。如果类中有动态内存变量出现,必须重写拷贝构造函数,且使用深拷贝;如果没有动态内存变量出现,可以不重写拷贝构造函数,使用默认的即可。
3. 析构函数
- 概念和作用
- 析构函数用于对对象进行善后处理,比如释放对象所占用的资源,如指针所指向的内存等。
- 特点
- 无返回值:和构造函数一样,析构函数不需要返回任何值。
- 函数名:~类名:这是析构函数的命名规则,通过在类名前面加上
~
来标识。 - 必须存在于public中(通常情况):这样系统才能在对象生命周期结束时自动调用析构函数。
- 只有一个,不能重载:每个类只能有一个析构函数,因为它的作用是明确的,就是在对象销毁时进行资源清理。
- 不可以主动调用(通常情况):析构函数是由系统自动执行的,当对象被释放时,系统会自动调用析构函数。不过在一些特殊情况下,比如使用
placement new
时,可能需要手动调用析构函数,但这是比较复杂的情况。 - 不用于销毁static数据成员:因为static数据成员是属于类的,它的生命周期和类的生命周期相同,不需要在对象销毁时进行处理。
- 按成员初始化顺序逆序销毁:与构造函数初始化数据成员的顺序相反,析构函数在执行时,会按照成员初始化顺序的逆序销毁对象的成员。
- 默认析构函数(合成析构函数)
- 自动生成条件:如果没有在类中给出析构函数,编译器自动生成一个默认的析构函数。这个默认析构函数是无参数的,并且函数体为空。
- 消失条件:当手动给出析构函数时,默认的析构函数就会消失。当类中有动态内存的变量时,不能使用默认析构函数,必须手动设计析构函数。例如:
class Cat
{
private:
char* name;
public:
~Cat();
};
Cat::~Cat()
{
if(name)//先判断成员是否为空
{
delete[] name;
name = nullptr;
}
}
4. 内联函数
- 概念和作用
- 内联函数是一种以空间换时间的优化手段。它的作用是在编译阶段将函数体直接嵌入到调用该函数的地方,避免了函数调用的开销(如压栈、弹栈等操作),从而提高程序的执行效率。
- 与构造函数、析构函数、拷贝构造函数的关系
- 在构造函数、析构函数和拷贝构造函数中,如果使用
=default
且在类内定义时,这些函数会被隐式地声明为内联函数。例如,当我们定义一个类的默认构造函数为=default
且在类内时:
- 在构造函数、析构函数和拷贝构造函数中,如果使用
class MyClass {
public:
MyClass() = default; // 这个默认构造函数是内联的
};
同样,如果析构函数或拷贝构造函数在类内使用=default
,它们也会是内联函数。这种内联的特性在某些情况下可以提高程序的性能,但也会增加代码的体积,需要根据具体情况权衡使用。
结语
感谢您的阅读!期待您的一键三连!欢迎指正!