为什么对于类的const成员,只能使用初始化列表,而不能在构造函数内部进行赋值操作
2017-12-29 11:23
561 查看
结论:对于类的const成员,只能使用初始化列表,而不能在构造函数内部进行赋值操作。
原因如下:
1、构造函数不能被声明为const函数,因此当我们创建一个类的const对象时,直到构造函数完成初始化的过程,对象才真正取得其“常量”的属性,因此,构造函数在const对象的构造过程中可以向其写值;见C++ primer P235;
2、初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。因此,由于常量只能初始化不能赋值,所以常量成员必须使用初始化列表;[1]
(当然你可以在类定义的时候,就对const成员变量进行赋值: class A{const int a = 1;};但是这样操作的话,这个变量就失去了意义,即基于这个类生成的所有对象a的值都为1!
再更进一步的讲,其实非const变量也可以在类定义的时候就进行赋值的操作,但是static变量不可以!)
3、主要是性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。[1]
#include <iostream>
using namespace std;
class Test1
{
public:
Test1() {cout << "Construct Test1" << endl ;} // 无参构造函数
Test1(const Test1& t1) // 拷贝构造函数
{
cout << "Copy constructor for Test1" << endl ;this->a = t1.a ;
}
Test1& operator = (const Test1& t1) // 重载赋值运算符(也称为重载赋值函数)
{
cout << "assignment for Test1" << endl ;
this->a = t1.a ;
return *this;
}
private:
int a ;
};
class Test2
{
public:
Test1 test1; //此处第二次调用Test 1的construct;
//std::cout<<"进入Test2 的构造函数"<<std::endl; //类中只能包含成员变量和成员函数!
Test2(Test1 &t1){test1 = t1 ;} //此处的“=”调用重载的“=”操作符;
};
int main(){
Test1 t1; //此处第一次调用Test1的construct;
cout<<"end of fist construct t1"<<endl;
Test2 t2(t1);
}
输出为:Construct Test1
end of fist construct t1
Construct Test1
assignment for Test1解释一下:
第一行输出对应调用代码中第一行,构造一个Test1对象
第三行输出对应Test2构造函数中的代码,用默认的构造函数初始化对象test1 // 这就是所谓的初始化阶段
第四行输出对应Test2的赋值运算符,对test1执行赋值操作 // 这就是所谓的计算阶段
如果使用初始化列表来实现Test2的构造函数;class Test2
{
public:
Test1 test1 ;
Test2(Test1 &t1):test1(t1){}
};输出为:
第一行输出对应 调用代码的第一行
第三行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。
所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表
参考:
[1] 初始化列表
原因如下:
1、构造函数不能被声明为const函数,因此当我们创建一个类的const对象时,直到构造函数完成初始化的过程,对象才真正取得其“常量”的属性,因此,构造函数在const对象的构造过程中可以向其写值;见C++ primer P235;
2、初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。因此,由于常量只能初始化不能赋值,所以常量成员必须使用初始化列表;[1]
(当然你可以在类定义的时候,就对const成员变量进行赋值: class A{const int a = 1;};但是这样操作的话,这个变量就失去了意义,即基于这个类生成的所有对象a的值都为1!
再更进一步的讲,其实非const变量也可以在类定义的时候就进行赋值的操作,但是static变量不可以!)
3、主要是性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。[1]
#include <iostream>
using namespace std;
class Test1
{
public:
Test1() {cout << "Construct Test1" << endl ;} // 无参构造函数
Test1(const Test1& t1) // 拷贝构造函数
{
cout << "Copy constructor for Test1" << endl ;this->a = t1.a ;
}
Test1& operator = (const Test1& t1) // 重载赋值运算符(也称为重载赋值函数)
{
cout << "assignment for Test1" << endl ;
this->a = t1.a ;
return *this;
}
private:
int a ;
};
class Test2
{
public:
Test1 test1; //此处第二次调用Test 1的construct;
//std::cout<<"进入Test2 的构造函数"<<std::endl; //类中只能包含成员变量和成员函数!
Test2(Test1 &t1){test1 = t1 ;} //此处的“=”调用重载的“=”操作符;
};
int main(){
Test1 t1; //此处第一次调用Test1的construct;
cout<<"end of fist construct t1"<<endl;
Test2 t2(t1);
}
输出为:Construct Test1
end of fist construct t1
Construct Test1
assignment for Test1解释一下:
第一行输出对应调用代码中第一行,构造一个Test1对象
第三行输出对应Test2构造函数中的代码,用默认的构造函数初始化对象test1 // 这就是所谓的初始化阶段
第四行输出对应Test2的赋值运算符,对test1执行赋值操作 // 这就是所谓的计算阶段
如果使用初始化列表来实现Test2的构造函数;class Test2
{
public:
Test1 test1 ;
Test2(Test1 &t1):test1(t1){}
};输出为:
Construct Test1 end of fist construct t1 Copy constructor for Test1
第一行输出对应 调用代码的第一行
第三行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。
所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表
参考:
[1] 初始化列表
相关文章推荐
- C++的const和引用只能在初始化列表里初始化而不能在构造函数体内赋值初始化
- C++中使用初始化列表比在构造函数中对成员变量赋值更高效
- C++构造函数对类成员变量初始化,使用初始化列表和构造函数内部直接赋值 的区别
- C++中使用初始化列表比在构造函数中对成员变量赋值更高效
- c++中什么类型的成员变量只能在构造函数的初始化列表中进行
- const成员或者引用成员必须使用构造函数初始化列表的方式
- 为什么尽量使用初始化而不要在构造函数里赋值
- 【C++】类的成员初始化表与构造函数内赋值操作
- C++有哪几种情况只能用初始化列表,而不能用赋值?
- 从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数
- 九、构造函数和析构函数(三) 初始化列表、对象成员初始化、const,引用成员初始化
- C++有哪几种情况只能用初始化列表,而不能用赋值?
- C/C++ 通过初始化列表和构造函数内赋值初始化成员变量的区别
- 关于SubSonic3.0插件使用Json反序列化获得的实体进行更新操作时,只能执行添加而不能执行修改(编辑)操作的处理
- C++有哪几种情况只能用初始化列表,而不能用赋值?
- C/C++ 通过初始化列表和构造函数内赋值初始化成员变量的区别
- 没有默认构造函数的问题, 对于类成员变量有别的类成员,则在类 构造函数中也必须对那个类成员变量进行初始化,除非那个类 成员有默认构造函数
- c++中子对象的初始化可在复合类的构造函数的函数体内进行吗?还是子对象的初始化只能在初始化列表中进行?
- 关于SubSonic3.0插件使用Json反序列化获得的实体进行更新操作时,只能执行添加而不能执行修改(编辑)操作的处理
- 何时构造函数必须使用初始化列表而不是赋值