构造函数后面的冒号后初始化列表
多个初始化之间用逗号隔开,另外初始化是按照参数声明的顺序,而不是按照你写得顺序进行初始化的。
条款12: 尽量使用初始化而不要在构造函数里赋值
看这样一个模板,它生成的类使得一个名字和一个t类型的对象的指针关联起来。
template<class t>
class namedptr {
public:
namedptr(const string& initname, t *initptr);
...
private:
string name;
t *ptr;
};
(因为有指针成员的对象在进行拷贝和赋值操作时可能会引起指针混乱(见条款11),namedptr也必须实现这些函数(见条款2))
在写namedptr构造函数时,必须将参数值传给相应的数据成员。有两种方法来实现。第一种方法是使用成员初始化列表:
template<class t>
namedptr<t>::namedptr(const string& initname, t *initptr )
: name(initname), ptr(initptr)
{}
第二种方法是在构造函数体内赋值:
template<class t>
namedptr<t>::namedptr(const string& initname, t *initptr)
{
name = initname;
ptr = initptr;
}
两种方法有重大的不同。
从纯实际应用的角度来看,有些情况下必须用初始化。特别是const和引用数据成员只能用初始化,不能被赋值。所以,如果想让namedptr<t>对象不能改变它的名字或指针成员,就必须遵循条款21的建议声明成员为const:
template<class t>
class namedptr {
public:
namedptr(const string& initname, t *initptr);
...
private:
const string name;
t * const ptr;
};
这个类的定义要求使用一个成员初始化列表,因为const成员只能被初始化,不能被赋值。
如果namedptr<t>对象包含一个现有名字的引用,情况会非常不同。但还是要在构造函数的初始化列表里对引用进行初始化。还可以对名字同时声明const和引用,这样就生成了一个其名字成员在类外可以被修改而在内部是只读的对象。
template<class t>
class namedptr {
public:
namedptr(const string& initname, t *initptr);
...
private:
const string& name; // 必须通过成员初始化列表
// 进行初始化
t * const ptr; // 必须通过成员初始化列表
// 进行初始化
};
然而前面最初的类模板不包含const和引用成员。即使这样,用成员初始化列表还是比在构造函数里赋值要好。这次的原因在于效率。当使用成员初始化列表时,只有一个string成员函数被调用。而在构造函数里赋值时,将有两个被调用。为了理解为什么,请看在声明namedptr<t>对象时都发生了些什么。
对象的创建分两步:
1. 数据成员初始化。(参见条款13)
2. 执行被调用构造函数体内的动作。
(对有基类的对象来说,基类的成员初始化和构造函数体的执行发生在派生类的成员初始化和构造函数体的执行之前)
对namedptr类来说,这意味着string对象name的构造函数总是在程序执行到namedptr的构造函数体之前就已经被调用了。问题只在于:string的哪个构造函数会被调用?
这取决于namedptr类的成员初始化列表。如果没有为name指定初始化参数,string的缺省构造函数会被调用。当在namedptr的构造函数里对name执行赋值时,会对name调用operator=函数。这样总共有两次对string的成员函数的调用:一次是缺省构造函数,另一次是赋值。
相反,如果用一个成员初始化列表来指定name必须用initname来初始化,name就会通过拷贝构造函数以仅一个函数调用的代价被初始化。
即使是一个很简单的string类型,不必要的函数调用也会造成很高的代价。随着类越来越大,越来越复杂,它们的构造函数也越来越大而复杂,那么对象创建的代价也越来越高。养成尽可能使用成员初始化列表的习惯,不但可以满足const和引用成员初始化的要求,还可以大大减少低效地初始化数据成员的机会。
换句话说,通过成员初始化列表来进行初始化总是合法的,效率也决不低于在构造函数体内赋值,它只会更高效。另外,它简化了对类的维护(见条款m32),因为如果一个数据成员以后被修改成了必须使用成员初始化列表的某种数据类型,那么,什么也不用变。
但有一种情况下,对类的数据成员用赋值比用初始化更合理。这就是当有大量的固定类型的数据成员要在每个构造函数里以相同的方式初始化的时候。例如,这里有个类可以用来说明这种情形:
class manydatambrs {
public:
// 缺省构造函数
manydatambrs();
// 拷贝构造函数
manydatambrs(const manydatambrs& x);
private:
int a, b, c, d, e, f, g, h;
double i, j, k, l, m;
};
假如想把所有的int初始化为1而所有的double初始化为0,那么用成员初始化列表就要这样写:
manydatambrs::manydatambrs()
: a(1), b(1), c(1), d(1), e(1), f(1), g(1), h(1), i(0),
j(0), k(0), l(0), m(0)
{ ... }
manydatambrs::manydatambrs(const manydatambrs& x)
: a(1), b(1), c(1), d(1), e(1), f(1), g(1), h(1), i(0),
j(0), k(0), l(0), m(0)
{ ... }
这不仅仅是一项讨厌而枯燥的工作,而且从短期来说它很容易出错,从长期来说很难维护。
然而你可以利用固定数据类型的(非const, 非引用)对象其初始化和赋值没有操作上的不同的特点,安全地将成员初始化列表用一个对普通的初始化函数的调用来代替。
class manydatambrs {
public:
// 缺省构造函数
manydatambrs();
// 拷贝构造函数
manydatambrs(const manydatambrs& x);
private:
int a, b, c, d, e, f, g, h;
double i, j, k, l, m;
void init(); // 用于初始化数据成员
};
void manydatambrs::init()
{
a = b = c = d = e = f = g = h = 1;
i = j = k = l = m = 0;
}
manydatambrs::manydatambrs()
{
init();
...
}
manydatambrs::manydatambrs(const manydatambrs& x)
{
init();
...
}
因为初始化函数只是类的一个实现细节,所以当然要把它声明为private成员。
请注意static类成员永远也不会在类的构造函数初始化。静态成员在程序运行的过程中只被初始化一次,所以每当类的对象创建时都去“初始化”它们没有任何意义。至少这会影响效率:既然是“初始化”,那为什么要去做多次?而且,静态类成员的初始化和非静态类成员有很大的不同,这专门有一个条款m47来说明。
文章摘自http://topic.csdn.net/t/20041011/10/3443125.html
构造函数后面的冒号后初始化列表相关推荐
- c++构造函数成员初始化中赋值和初始化列表两种方式的区别
先总结下: 由于类成员初始化总在构造函数执行之前 1)从必要性: a. 成员是类或结构,且构造函数带参数:成员初始化时无法调用缺省(无参)构造函数 b. 成员是常量或引用:成员无法赋值,只能被初始化 ...
- C++类和对象成员函数,静态成员,构造函数和析构函数、初始化列表
类和对象 数据成员的访问 定义成员函数 调用成员函数 私有成员函数 构造函数和析构函数 构造函数成员初始化列表 在构造函数中使用new的注意事项 this指针 const成员函数 运算符重载 友元函数 ...
- 派生类参数初始化列表和基类构造函数顺序
今天被问到了一个问题,随便回了一句,父类还没有构建,怎么能初始化父类的成员. 派生类构造函数的参数初始化列表,为什么不能初始化基类的成员? 例如下面的是不可以的 class Rectangle : p ...
- C++ 初始化列表详解
目录 1.什么是初始化列表 2.什么时候需要使用初始化列表? 3.初始化列表的效率 4.初始化列表的初始化顺序 1.什么是初始化列表 class A { public:A(int value):m_d ...
- [转载] C++11初始化列表与参数列表的作用
参考链接: C++ : List的不同初始化方法 最近遇到了一个程序,一开始有些莫名其妙,最后经过思考.猜想.验证的过程,将其总结如下: 首先先上代码: 函数声明如下: class Controlle ...
- C++必须使用【初始化列表】初始化数据成员的三种情况
类对象的构造顺序是这样的: 1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员: 2.进入构造函数后在构造函数中执行一般赋值与计算. 使用初始化列表有两个原因: 原因1.必须这样做: < ...
- C++:用成员初始化列表对数据成员初始化
1.在声明类时,对数据成员的初始化工作一般在构造函数中用赋值语句进行. 例如: class Complex{private:double real;double imag;public:Comple ...
- 对象的初始化列表const变量的初始化
当B类中有A类并且A类还有自己的有参数构造函数的时候,那这时若是B再使用默认构造函数就不能使用了,因为B不能对A进行初始化: #include <iostream> using names ...
- 【黑马程序员 C++教程从0到1入门编程】【笔记4】C++核心编程(类和对象——封装、权限、对象的初始化和清理、构造函数、析构函数、深拷贝、浅拷贝、初始化列表、友元friend、运算符重载)
黑马程序员C++教程 文章目录 4 类和对象(类属性[成员属性],类函数[成员函数]) 4.1 封装 4.1.1 封装的意义(三种权限:public公共.protected保护.private私有)( ...
最新文章
- eclipse中运行c++控制台没输出
- 阿里面试这样问:redis 为什么把简单的字符串设计成 SDS?
- 针对杂乱环境下抓取物体的机器人学习
- MyBatis入门学习教程-调用存储过程
- VTK:PolyData之MergeSelections
- ip, tcp, udp, icmp header
- jQuery 设计和自定义一个带展开动画效果的导航栏
- hdu 3917 Road constructions 最大权闭合子图
- java中synized_ConcurrentHashMap和Collection s.SynizedMap(Map)
- CodeSheep谈前后端选择
- Flex 国际化使用
- 【Paper】AAAI 2020 故事生成模型 之 角色一致性
- c语言C的ascii码是多少,c的ascii码值是多少
- PI系统在DCS中的应用
- masm32踩坑总结
- 使用python将pdf文件转化为word文件
- Using的三种使用方法
- 罗技键盘连计算机,罗技键盘怎么连接电脑
- 单片机原理及应用 实验一 计数显示器
- 拒绝做工具小子—编写Python漏洞验证脚本
热门文章
- 大数据的说法 正确的是_数据量——让数据分析师永远头疼的指标
- 2019款享域视频_钱都花哪了?单日投放最高2443款,复盘2019年买量最烧钱的100款手游...
- mysql gui tools ojdbc14.jar_[java]OJDBC版本区别 [ojdbc14.jar,ojdbc5.jar和ojdbc6.jar的区别]
- python使用scrapy_Python实现从脚本里运行scrapy的方法
- 全网抢夺“刘畊宏女孩”
- 期权这块「饼」,互联网人吃不下去了
- 芬兰诺基亚获得三个欧洲市场的5G订单
- “阿里女员工被侵害”案最新进展:两人涉嫌强制猥亵
- 这届年轻人,连泡面也买不起了
- 英特尔Bridge技术加持下 Windows 11 PC也能运行手机应用