8.3对象的生命周期
1.创建对象
1.1编写构造函数
从语法上讲,构造函数是与类同名的方法。**构造函数没有返回类型,可以有也可以没有参数,没有参数的构造函数称为默认构造函数。**可以是无参构造函数,也可以让所有参数都使用默认值。有些情况下,必须提供默认构造函数, 如果不提供,就会导致编译器错误.
1.2使用构造函数
在栈中使用构造函数
className myClass(5), myClass2(4);
className myClass{5}, myClass2{4};
在自由存储区中使用构造函数
略
1.3提供多个构造函数
略
1.4默认构造函数
默认构造函数没有参数, 也称为无参构造函数.
什么时候需要默认构造函数
略
如何编写默认构造函数
**如果声明了任何构造函数, 编译器就不会载自动生成默认构造函数. **
显式默认的默认构造函数
class Foo{
public:
Foo() = delault;
};
显式删除的默认构造函数
class Foo{
public:
Foo() = delete;
};
如果类的数据成员具有删除的默认构造函数,则该类的默认构造函数也会自动删除。
1.5构造函数初始化器
构造函数初始化器允许在创建数据成员时赋初值,这样做比在后面赋值效率高。
如果类的数据成员是具有默认构造函数的类的对象,则不必在构造函数初始化器中显式初始化对象。
关于构造函数初始化器要特别注意,数据成员的初始化顺序如下: 按照它们在类定义中出现的顺序,而不是在构造函数初始化器中的顺序。
1.6拷贝构造函数(Copy Constructor)
所创建的对象是另一个对象的副本.
何时调用拷贝构造函数
略
显式调用拷贝构造函数
略
按引用传递对象
略
显式默认或显式删除的拷贝构造函数
className(const className& src) = default;
className(const className& src) = delete;
1.7初始化列表构造函数
初始化列表构造函数(initializer-list constructor)将std::initializer_list<T>
作为第一个参数, 并且没有任何其他参数(或者其他参数具有默认值).
class Foo{
public:
Foo(initializer_list<double> args)
{
vec.assign(args);
}
private:
vector<double> vec{};
};
1.8委托构造函数
委托构造函数(delegating constructor)允许构造函数调用一个类的其他构造函数. 然而, 这个调用不能放在构造函数体内, 而必须放在构造函数初始化器中, 且必须是列表中唯一的成员初始化器.
1.9转换构造函数和显式构造函数
class Foo{
public:
Foo() = default;
Foo(double initialVal);// transfer double to Foo
Foo(string_view initialVal);// transfer string_view to Foo
Foo(const Foo& src);
private:
int m_num{};
};
建议将任何可以使用单个参数调用的构造函数标记为 explicit
,以避免意外的隐式转换,除非此类转换是有用的。
从C++20开始,可以将布尔参数传递给explicit,以将其转换为条件 explicit。
explicit(true) Foo(0.0f);
总结编译器生成的构造函数
略
2.销毁对象
略
3.对象赋值
拷贝赋值运算符(Copy assifnment operator)
3.1声明赋值运算符
class Foo{
public:
Foo& operator=(const Foo& rhs);
};
3.2定义赋值运算符
Foo::Foo(const Foo& rhs)
{
if(this == &rhs){
return *this;
}
// ...
}
3.3显式默认或显式删除的赋值运算符
Foo operator=(const Foo& rhs) = default;
Foo operator=(const Foo& rhs) = delete;
4.编译器生成的拷贝构造函数和拷贝赋值运算符
略
5.赋值和复制的区别
5.1按值返回对象
略
5.2拷贝构造函数和对象成员
如果某个对象包含其他对象,编译器生成的拷贝构造函数会递归调用每个被包含对象的拷贝构造函数。当编写自己的拷贝构造函数时,可使用前面所示的构造函数初始化器提供相同的语义。如果在构造函数初始化器中省略某个数据成员,在执行构造函数体内的代码之前,编译器将对该成员执行默认的初始化(为对象调用默认构造函数)。这样,在执行构造函数体时,所有数据成员都已经初始化。