c++构造函数成员初始化中赋值和初始化列表两种方式的区别
先总结下:
由于类成员初始化总在构造函数执行之前
1)从必要性:
a. 成员是类或结构,且构造函数带参数:成员初始化时无法调用缺省(无参)构造函数
b. 成员是常量或引用:成员无法赋值,只能被初始化
2)从效率上:
如果在类构造函数里赋值:在成员初始化时会调用一次其默认的构造函数,在类构造函数里又会调用一次成员的构造函数再赋值
如果在类构造函数使用初始化列表:仅在初始化列表里调用一次成员的构造函数并赋值
CMyClass::CMyClass() {
// 使用赋值操作符
// CString::operator=(LPCTSTR);
m_str = _T( "yada yada "); }
//使用类成员列表
// and constructor CString::CString(LPCTSTR)
CMyClass::CMyClass() : m_str(_T( "yada yada "))
{ }
在 它们之间有什么不同吗?是的。编译器总是确保所有成员对象在构造函数体执行之前初始化,因此在第一个例子中编译的代码将调用CString:: Cstring来初始化m_str,这在控制到达赋值语句前完成。在第二个例子中编译器产生一个对CString:: CString(LPCTSTR)的调用并将 "yada yada "传递给这个函数。结果是在第一个例子中调用了两个Cstring函数(构造函数和赋值操作符),而在第二个例子中只调用了一个函数。在 Cstring的例子里这是无所谓的,因为缺省构造函数是内联的,Cstring只是在需要时为字符串分配内存(即,当你实际赋值时)。但是,一般而言, 重复的函数调用是浪费资源的,尤其是当构造函数和赋值操作符分配内存的时候。在一些大的类里面,你可能拥有一个构造函数和一个赋值操作符都要调用同一个负 责分配大量内存空间的Init函数。在这种情况下,你必须使用初始化列表,以避免不要的分配两次内存。在内部类型如ints或者longs或者其它没有构 造函数的类型下,在初始化列表和在构造函数体内赋值这两种方法没有性能上的差别。不管用那一种方法,都只会有一次赋值发生。
当考虑初始化列表的问题时,有一个奇怪的特性应该注意,它是关于C++初始化类成员的,它们是按照声 明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
class CMyClass {
CMyClass(int x, int y);
int m_x;
int m_y;
}; CMyClass::CMyClass(int i) : m_y(i), m_x(m_y)
{ }
你 可能以为上面的代码将会首先做m_y=I,然后做m_x=m_y,最后它们有相同的值。但是编译器先初始化m_x,然后是m_y,,因为它们是按这样的顺 序声明的。结果是m_x将有一个不可预测的值。我的例子设计来说明这一点,然而这种bug会更加自然的出现。有两种方法避免它,一个是总是按照你希望它们 被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。
很多的人对中构造函数寝初始化很多的困惑,对冒号后初始化不是太明白,总搞不清楚它们之间的区别,我想把我对这个问题的理解和看法和大家讨论讨 论。
在程序中定义变量并初始化的机制中,有两种形式,一个是我们传统的初始化的形式,即赋值运算符赋值,还有一种是括号赋值,如:
int a=10;
char b='r';\\赋值运算符赋值
int a(10);\
char b('r');\\括号赋值
以上定义并初始化的形式是正确的,可以通过编译,但括号赋值只能在变量定义并初始化中,不能用在变量定义后再赋值,这是和赋值运算符赋值的不同之处,如:
(1)
int a; \\先定义一个变量
......
a=10; \\根据需要赋值
(2)
int b; \\先定义一个变量
......
b(10); \\和(1)一样根据需要赋值
(1)是可以用通过编译,定义一个变量a但并没有初始化,在需要变量a的时候,通过赋值运算符把10赋给a,而在(2)中,是通过括号把10赋值给b,但 编译系统认为
这是一个函数的调用,函数名为b,10为实际参数,所以编译错误。因此,括号赋值只用在定义变量并初始化中。
现在我们来看构造函数中冒号初始化和函数初始化的问题,类构造函数的作用是创建一个类的对象时,调用它来构造这个类对象的数据成员,一要给出此数据成员分 配内存空间,二是要给函数数据成员初始化,构造数据成员是按数据成员在类中声明的顺序进行构造。
冒号初始化与函数体初始化的区别在于:
冒号初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),那么 分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行。
对于在函数中初始化,是在所有的数据成员被分配内存空间后才进行的。
这样是有好处的,有的数据成员需要在构造函数调入之后函数体执行之前就进行初始化如引用数据成员,常量数据成员和对象数据成员,看下面的一段程序:
class student
{public :
student ()
protected:
const int a;
int &b;
} student ::student (int i,int j)
{
a=i;
b=j;
}
在Student类中有两个数据成员,一个是常量数据成员,一个是引用数据成员,并且在构造函数中初始化了这两个数据成员,但是这并不能通过编译,因为常 量初始化时必须赋值,它的值是不能再改变的,与常量一样引用初始化也需要赋值,定义了引用后,它就和引用的目标维系在了一起,也是不能再被赋值的。所以C
++":"后初始化的机制,使引用和常量数据成员变为可能的,Student类的构造函数应为:
student ::student(int i,int j):a(i),b(j){}
class teach
{
public :
teach(char *p="name",int a=0);
protected:
char name[30];
int age;
};
teach::teach(char*p, int a)
{
strcpy(name ,p);
age = a;
cout << name << endl;
}
class student
{
public:
student (char *p="name");
protected:
char name[30];
teach teacher;
};
student::student(char *p)
{
strcpy(name,p);
cout << name << endl;
}
在上面的程序中通不过编译,编译系统会告诉你teacher这个类对象缺默认构造函数,因为在teach 类中没有定义默认的构造函数。那么带参数的构造函数怎么进行构造呢?通过我们前面提到的冒号赋值。那它的构造函数应该是:
student::student(char *p,char *pl,int ag):teacher(pl,ag)
{
strcopy(name,p);
cont<<name<<endl;
}
就是说在没有默认构造函数的时候,如果一个类对象是另一个类的数据成员,那么初始化这个数 据成员,就应该放到冒号后面。这样可以带参数。在类的定义中,如:
protected;
char name[30];
teach teacher
类对象是不能带参数的,因为它只是声明。
c++构造函数成员初始化中赋值和初始化列表两种方式的区别的更多相关文章
- JQuery中阻止事件冒泡的两种方式及其区别
JQuery 提供了两种方式来阻止事件冒泡. 方式一:event.stopPropagation(); $("#div1").mousedown(function(event){ ...
- strus2中获取表单数据 两种方式 属性驱动 和模型驱动
strus2中获取表单数据 两种方式 属性驱动 和模型驱动 属性驱动 /** * 当前请求的action在栈顶,ss是栈顶的元素,所以可以利用setValue方法赋值 * 如果一个属性在对象栈,在页面 ...
- 怎样在Android开发中FPS游戏实现的两种方式比较
怎样在Android开发中FPS游戏实现的两种方式比较 如何用Android平台开发FPS游戏,其实现过程有哪些方法,这些方法又有哪些不同的地方呢?首先让我们先了解下什么是FPS 英文名:FPS (F ...
- HTML中设置背景图的两种方式
HTML中设置背景图的两种方式 1.background background:url(images/search.png) no-repeat top; 2.background-image ...
- [Android] Android ViewPager 中加载 Fragment的两种方式 方式(二)
接上文: https://www.cnblogs.com/wukong1688/p/10693338.html Android ViewPager 中加载 Fragmenet的两种方式 方式(一) 二 ...
- [Android] Android ViewPager 中加载 Fragment的两种方式 方式(一)
Android ViewPager 中加载 Fragmenet的两种方式 一.当fragment里面的内容较少时,直接 使用fragment xml布局文件填充 文件总数 布局文件:view_one. ...
- .Net 中读写Oracle数据库常用两种方式
.net中连接Oracle 的两种方式:OracleClient,OleDb转载 2015年04月24日 00:00:24 10820.Net 中读写Oracle数据库常用两种方式:OracleCli ...
- 【转】在Android Studio中下载Android SDK的两种方式(Android Studio3.0、windows)
在Android Studio中下载Android SDK的两种方式(Android Studio3.0.windows) 方式一.设置HTTP Proxy1. 打开Settings2. 点击HTTP ...
- 关于JSP页面中的pageEncoding和contentType两种属性的区别
转自:http://blog.csdn.net/dragon4s/article/details/6604624 JSP指令标签中<%@ page contentType="text/ ...
随机推荐
- Visual Studio语言设置
按照的是中文的visual studio,用起来很不方便,因为程序员的都是英文版,平时交流时也是英文的名字 转换语言时发现只有中文和跟随windows系统的设置 官方给的文档看的不是很清楚 查阅资料后 ...
- Windows重启显卡驱动热键说明
Windows 有一个秘密的快捷键,可以重启显卡驱动程序.如果你的电脑经常“冻屏”,可以在重启电脑之前试试这个快捷键,它可以修复冻屏,否则就只能强制重启电脑了. 这个组合快捷键将重启 Win10 和 ...
- LeetCode算法题-House Robber(Java实现)
这是悦乐书的第187次更新,第189篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第46题(顺位题号是198).你是一个专业的强盗,计划在街上抢劫房屋. 每个房子都藏着一 ...
- Linux 小知识翻译 - 「UTC 和 JST」
这周解释下「UTC 和 JST」. 地球上的国家之间是存在时差的.然而互联网是遍布全球的网络,跨国境的数据传输是很频繁的.因此,互联网上的服务器就需要一个共通的时间. 实际上,互联网上的服务器,大部分 ...
- WPF调用zxing生成二维码
1.登录http://zxingnet.codeplex.com/,下载对应.net版本的zxing库 2.引入zxing.dll 3.新建界面控件 using System; using Syste ...
- ASP.NET API Helper Page 创建并生成相关帮助文档
创建API项目 修改原工程文件,该行为是为了避免和引入第三方API工程文件冲突 修改发布设置 引入需要生成文档的相关文件,将第三方API依赖的相关文件(XML文件非常重要,是注释显示的关键),复制到文 ...
- Maven打包相关插件集合
参考:https://www.cnblogs.com/selier/p/9510326.html
- VM虚拟机ubantu自适应屏幕大小
1.菜单栏安装VMware-Tool sudo ./wmware-install.pl 2.sudo apt-get install open-vm-tools装完这两个就可以,有些人只安装了第一个, ...
- spring boot启动报错
Exception encountered during context initialization - cancelling refresh attempt: org.springframewor ...
- Windows10下使用python+selenium实现谷歌浏览器的自动控制
第一 谷歌浏览器一直是开发人员最喜欢的浏览器,python爬虫在进行抓包时尤其好用,今天为大家带来python+selenium进行自动化控制的安装教程 安装selenium windows下在cmd ...