先总结下:

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

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. MyBatis笔记----报错Exception in thread "main" org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.ij34.model.UserMapper.selectUser

    信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@41cf53f9: startup ...

  2. 剑指offer(java版)【转】

    面试题 2 :实现单例模式 1. 饿汉式单例类 public class SingletonClass { private static final SingletonClass instance=n ...

  3. C#获取日期的星期名称

    private string GetWeekName(DayOfWeek week) { string weekName = ""; switch (week) { case Da ...

  4. Linux atop 监控系统状态

    atop是一个功能非常强大的linux服务器监控工具,它的数据采集主要包括:CPU.内存.磁盘.网络.进程等,并且内容非常的详细,特别是当那一部分存在压力它会以特殊的颜色进行展示,如果颜色是红色那么说 ...

  5. jQuery入门(1)

    1.了解jQuery与JavaScript的区别 css --你的长相啦 Html --躯干 js --运动神经 jQuery就是对JavaScript的一个拓展,封装,就是让JavaScript更好 ...

  6. MySql基本使用方法

    一,基本概念 1, 常用的两种引擎:         (1) InnoDB        a,支持ACID,简单地说就是支持事务完整性.一致性:         b,支持行锁,以及类似ORACLE的一 ...

  7. 注入攻击(SQL注入)

    注入攻击是web安全领域中一种最为常见的攻击方式.注入攻击的本质,就是把用户输入的数据当做代码执行.这里有两个关键条件,第一是用户能够控制输入,第二个就是原本程序要执行的代码,将用户输入的数据进行了拼 ...

  8. 操作系统常用调度算法(转载https://www.cnblogs.com/kxdblog/p/4798401.html)

    操作系统常用调度算法   在操作系统中存在多种调度算法,其中有的调度算法适用于作业调度,有的调度算法适用于进程调度,有的调度算法两者都适用.下面介绍几种常用的调度算法. 先来先服务(FCFS)调度算法 ...

  9. 【算法】LeetCode算法题-Longest Common Prefix

    这是悦乐书的第146次更新,第148篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第5题(顺位题号是14),给定一个随机的字符串数组,查找这些字符串元素的公共前缀字符串, ...

  10. 日志收集(ElasticSearch)串联查询 MDC

    之前写过将应用程序或服务程序产生的日志直接写入搜索引擎的博客 其中基本过程就是  app->redis->logstash->elasticsearch 整个链路过程  本来想将re ...