总结
- 初始化值的是创建变量时赋予变量一个值(不同于赋值的概念)
- 使用等号
=
初始化对象时是拷贝初始化,否则是直接初始化 - 直接初始化也可能调用拷贝构造函数,拷贝初始化可以不调用拷贝构造函数
初始化概念
对象是类的实例化,在内存中会占据一个一定大小的空间。创建一个对象分为两步:分配内存空间和初始化,刚刚分配的空间有可能包含脏数据,因此我们需要通过初始化函数(C++中指的是构造函数)对分配的空间进行正确地初始化以保证对象值的合法性。
初始化指的就是创建变量是赋予它一个值(不同于赋值的概念),类的构造函数控制其对象的初始化过程,无论何时只要类的对象被创建就会执行构造函数。
拷贝初始化与直接初始化
C++由于历史原因包含多种不同的初始化方式,我们可以简单地认为:如果使用等号=
初始化变量则执行的是拷贝初始化(编译器将等号右边的对象值拷贝到新创建的对象中去),不使用等号时使用的是直接初始化。
string s1 = "tomocat"; // 拷贝初始化
string s2("tomocat"); // 直接初始化
string s3(10, 'c'); // 直接初始化, s3内容为cccccccccc
// s4拷贝初始化
string s4 = string(10, 'c');
// 等价于
string temp = string(10, 'c');
string s4 = temp;
注意当我们使用直接初始化时(不用等号=
初始化变量),实际上是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数(当然也包括拷贝构造函数),当我们使用拷贝初始化时(使用=
初始化变量),我们要求编译器将右侧运算对象的值拷贝到正在创建的对象中,如果有需要的话还需要进行类型转换。
实战
尽管直接初始化和拷贝初始化的定义如上所示,但是由于直接初始化可能调用拷贝构造函数,拷贝初始化不一定调用拷贝构造函数,我们还是结合一些实例来看一下。我们定义如下的结构体:
struct Cat {
// 默认构造函数
Cat() {
printf("Cat()\n");
}
// 析构函数
~Cat() {
printf("~Cat()\n");
}
// 构造函数
Cat(const Cat &cat) {
printf("Cat(const Cat &cat)\n");
}
// 接受string对象的构造函数
Cat(std::string n) : name(n) {
printf("Cat(std::string n)\n");
}
// 拷贝赋值运算符
Cat& operator=(const Cat &cat) {
printf("Cat& operator=(const Cat &cat)\n");
return *this;
}
std::string name;
};
几种常用的初始化实现方式如下:
int main(void) {
// 拷贝初始化: 执行std::string到Cat的隐式类型转换
Cat cat1 = std::string("tomo");
printf("-------------------------------------------------------------------\n");
// 拷贝初始化: 调用拷贝构造函数而不是拷贝赋值运算符(因为这里是初始化而不是赋值)
Cat cat2 = cat1;
printf("-------------------------------------------------------------------\n");
// 直接初始化: 调用拷贝构造函数
Cat cat3(cat2);
printf("-------------------------------------------------------------------\n");
return 0;
}
输出信息如下:
Cat(std::string n)
-------------------------------------------------------------------
Cat(const Cat &cat)
-------------------------------------------------------------------
Cat(const Cat &cat)
-------------------------------------------------------------------
~Cat()
~Cat()
~Cat()
Reference
[1] https://blog.csdn.net/sirenxiaohuayuan/article/details/90142419