C++构造函数
深拷贝和浅拷贝
- 浅拷贝
- 就是对变量的简单按位复制内存
- 在C++中,默认的拷贝构造函数制作按位复制,也就是浅拷贝
- 深拷贝
- 深拷贝除了将所有的成员变量拷贝给新对象外,对于指针等指向外部资源的成员变量,还会为新对象分配一块新的内存,将指针指向的内容也拷贝一份,这样原有对象和新对象的内存都是互相独立的,避免两个对象互相影响和
double free的错误。 - 在C++中,深拷贝必须由程序员显式地实现
- 深拷贝除了将所有的成员变量拷贝给新对象外,对于指针等指向外部资源的成员变量,还会为新对象分配一块新的内存,将指针指向的内容也拷贝一份,这样原有对象和新对象的内存都是互相独立的,避免两个对象互相影响和
拷贝构造函数和赋值构造函数
- 拷贝构造函数
- 是对构造函数的重载,函数原型为 注意输入的参数应该是引用,如果是值传递的话,在值传递时就会触发拷贝构造函数,导致无限递归,造成崩溃。
1
myClass(const myClass & obj){} - 会在下面的情况被调用
- 用类的一个对象去初始化另一个对象时(
myClass obj2(obj1)和myClass obj3 = obj1两种写法都是) - 当函数的形参是类的对象时(也就是值传递),引用传递不会调用拷贝构造函数
- 当函数返回值是类的对象时
- 用类的一个对象去初始化另一个对象时(
- 是对构造函数的重载,函数原型为
- 赋值构造函数
- 是赋值操作符的重载,函数原型为
1
2
3
4myClass& operator=(const myClass & obj) {
...
return *this;
} - 会在用一个已有对象来给另一个已经创建好的对象赋值时被调用
- 是赋值操作符的重载,函数原型为
示例
1 | |
移动构造函数和移动赋值函数
与拷贝构造函数和赋值构造函数的区别在于输入的参数为右值引用,且移动构造和移动赋值函数会直接将资源的所有权转移到当前对象,省去拷贝的步骤,能够大幅提升效率。
1 | |
std::move的理解
std::move的作用是将参数转换为右值引用,以便利用移动构造函数或移动赋值运算符。- 移动语义的目的是避免不必要的拷贝,特别是在处理大型对象或资源密集型对象时。
- 移动后对象的状态应为有效但不确定(通常是安全但不可预测的),即移动后的对象处于“可析构”但“不可用”的状态。
std::move仅仅是一个类型转换,实际的移动操作由移动构造函数或移动赋值运算符完成。
delete,explicit和default
delete是C++11引入的新特性,在成员函数后面使用=delete修饰,表示禁用该函数。智能指针unique_ptr就是通过禁用拷贝构造函数来实现的。1
A& operator=(const A& a) = delete;explicit则在函数前面修饰,用于禁止隐式类型转换- 在 C++ 中,任何只有一个参数(或者除了第一个参数外,其它参数都有默认值)的构造函数,默认就是一个转换构造函数(converting constructor),它允许编译器在需要的时候把那个参数类型 隐式 地转换成该类的对象,例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14class A {
public:
A(int a) { a_ = a; }
~A() {}
private:
int a_;
};
int main() {
A a(1);
A b = 100; // 会触发隐式类型转换
return 0;
} - 但这样的隐式有时候并不是我们所期望的,可能只是代码写错了,为了避免这种情况,可以用
explicit修饰构造函数1
2
3
4
5class A {
public:
explicit A(int a) { a_ = a; }
...
} - 除了构造函数外,
explicit还可以用于修饰类型转换函数(C++11)1
2
3
4
5
6
7
8
9struct B {
explicit operator bool() const { return some_flag; }
};
B b;
if (b) { // 错误:operator bool() 是 explicit 的,不能隐式转换
}
bool x = static_cast<bool>(b); // OK:显式转换
- 在 C++ 中,任何只有一个参数(或者除了第一个参数外,其它参数都有默认值)的构造函数,默认就是一个转换构造函数(converting constructor),它允许编译器在需要的时候把那个参数类型 隐式 地转换成该类的对象,例如
default一般用于修饰构造函数,要求编译器生成默认的构造函数。- 对于普通的构造函数,如果类有成员变量,就按照它们的默认构造来初始化。
- 如果成员是内置类型(如
int、double),那它们不会被自动初始化,值是未定义的垃圾值。 - 如果成员是类类型(比如
std::string),会调用该成员的默认构造函数
- 如果成员是内置类型(如
- 对于默认的拷贝构造函数,逐成员拷贝,即调用每个成员的拷贝构造。
- 对于默认的赋值构造函数,逐成员赋值,即调用每个成员的赋值构造。
如果类中没有写任何构造函数,那么编译器会隐式地生成默认构造函数,如果你定义了一个有参构造函数,编译器就不会再生成默认构造函数了;但你可以用
= default要求编译器还是生成一个,这是default关键字最重要的应用场景。
1
2
3
4
5
6
7
8
9
10
11class A {
public:
A(int a) { a_ = a; }
~A() {}
private:
int a_;
};
int main() {
A a;
}
上面的代码会由于A没有无参数的构造函数而导致报错,可以在A中添加
1
A() = default;
解决,但是默认的构造函数并不会将a_初始化。
此外default也可以修饰拷贝构造函数。- 对于普通的构造函数,如果类有成员变量,就按照它们的默认构造来初始化。
C++构造函数
https://guts.homes/2025/06/12/cpp-constructor/