先总结下:

由于类成员初始化总在构造函数执行之前

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++构造函数成员初始化中赋值和初始化列表两种方式的区别的更多相关文章

  1. JQuery中阻止事件冒泡的两种方式及其区别

    JQuery 提供了两种方式来阻止事件冒泡. 方式一:event.stopPropagation(); $("#div1").mousedown(function(event){ ...

  2. strus2中获取表单数据 两种方式 属性驱动 和模型驱动

    strus2中获取表单数据 两种方式 属性驱动 和模型驱动 属性驱动 /** * 当前请求的action在栈顶,ss是栈顶的元素,所以可以利用setValue方法赋值 * 如果一个属性在对象栈,在页面 ...

  3. 怎样在Android开发中FPS游戏实现的两种方式比较

    怎样在Android开发中FPS游戏实现的两种方式比较 如何用Android平台开发FPS游戏,其实现过程有哪些方法,这些方法又有哪些不同的地方呢?首先让我们先了解下什么是FPS 英文名:FPS (F ...

  4. HTML中设置背景图的两种方式

    HTML中设置背景图的两种方式 1.background    background:url(images/search.png) no-repeat top; 2.background-image ...

  5. [Android] Android ViewPager 中加载 Fragment的两种方式 方式(二)

    接上文: https://www.cnblogs.com/wukong1688/p/10693338.html Android ViewPager 中加载 Fragmenet的两种方式 方式(一) 二 ...

  6. [Android] Android ViewPager 中加载 Fragment的两种方式 方式(一)

    Android ViewPager 中加载 Fragmenet的两种方式 一.当fragment里面的内容较少时,直接 使用fragment xml布局文件填充 文件总数 布局文件:view_one. ...

  7. .Net 中读写Oracle数据库常用两种方式

    .net中连接Oracle 的两种方式:OracleClient,OleDb转载 2015年04月24日 00:00:24 10820.Net 中读写Oracle数据库常用两种方式:OracleCli ...

  8. 【转】在Android Studio中下载Android SDK的两种方式(Android Studio3.0、windows)

    在Android Studio中下载Android SDK的两种方式(Android Studio3.0.windows) 方式一.设置HTTP Proxy1. 打开Settings2. 点击HTTP ...

  9. 关于JSP页面中的pageEncoding和contentType两种属性的区别

    转自:http://blog.csdn.net/dragon4s/article/details/6604624 JSP指令标签中<%@ page contentType="text/ ...

随机推荐

  1. SQL中常用系统函数

    --1 CONVERT(数据类型,表达式),CAST( 表达式 AS 数据类型) 转变数据类型--将数字转化为字符串SELECT CONVERT(varchar(2),12)+CONVERT(varc ...

  2. fedora 28/29 配置 C++ 环境

    最近 使用C++ 开发 更换机器的时候,还要重新配置一下 gnu 工具链.于是简单进行了安装了一下: yum install gcc yum install gcc-c++ yum install g ...

  3. Git Extensions 使用小结

    1.查看仓库 2.创建分支 然后会自动创建一个 Commit ,推送到远端分支即可. 3.合并分支 注意1.自动提交需要没有无法自动合并的冲突才行. 注意2.快进线指的是将别人的提交原封不动附加到自己 ...

  4. May 28. 2018 Week 22nd Monday

    Do one thing at a time, and do well. 一次只做一件事,并且要做到最好. Why is it that about 25% to 50% of people have ...

  5. java 对象属性复制,将一个对象的属性值赋值给另一个对象, 属性名需要相同

    import org.springframework.beans.BeanUtils; BeanUtils.copyProperties(源对象, 目标对象);

  6. 【Window Power Shell】介绍与使用

    Windows PowerShell 是专为系统管理员设计的新 Windows 命令行脚本环境,主要实现系统和应用程序管理自动化. 1.发展历史 在2002年,微软开始研究一个新的产品叫做”Monad ...

  7. 近日使用Taro框架的一点小心得

    1.yarn npm安装的包,跟权限问题有关,与网络也有关 2.Vue框架首先,是解决了view-model的问题,解放开发的双手,使得显示和数据和控制分开 3.当你觉得最近没有技术文章看时,就看收藏 ...

  8. mongoDB python 操作

    mongoDB python 操作 import pymongo mongo_client = pymongo.MongoClient(host="127.0.0.1",port= ...

  9. Django 中的 日志处理

    日志处理: 上线后必须使用 便于以后的 维护 管理 根据日志 处理 BUG 在 项目中 定义一个 存放日志的 文件夹 log 存放所有 等级 的 日志 配置: 将下面的日志的 配置 写入 django ...

  10. dicom学习文章

    https://blog.csdn.net/column/details/dicom.html https://blog.csdn.net/zssureqh/article/details/49231 ...