c++类构造函数理解(转载)

记录一下我对C++类构造函数的理解。

首先,构造函数分成两种,默认构造函数和非默认构造函数(好吧,就这么叫它)。

默认构造函数只能有一个,如果没有自定义构造函数,那么编译器将自动生成一个默认构造函数,当然这个构造函数不会做任何事情。如果程序定义了构造函数(包括默认和非默认),编译器都不再自动提供默认构造函数。

如class C, 对应自动生成的默认构造函数为C() { };

程序员可以自定义默认构造函数,而且只能定义一个默认构造函数。如定义class A.

Class A

{

public:

//       A() { }  //constructor1,
do nothing,默认构造函数

//       A(int
m=1):v1(m);//construct2,默认构造函数,提供了部分的默认初始化值

//       A(int m=1, int n=2):v1(m),
v2(n);//construct3, 默认构造函数,提供了所有的默认初始化值

//       A(int m);
//construct4,非默认构造函数, 提供一个参数

//       A(int m, int n);
//construct5, 非默认构造函数, 提供两个参数

private:

int v1;

int v2;

}

下面具体分析一下各种定义方式会带来的问题:

1)不定义任何构造函数,这个上面说过,编译器会自动构造一个默认构造函数,等价于定义了construct1。

2) 只定义construct2, 可以正确编译运行。但是无法以形式A a(1,2)定义对象,这是显然的。

3)    只定义construct3, 可以正确编译运行。

4)    定义2个及以上默认构造函数的情况。

想来同时提供construct2和construct3是不会矛盾的,而且符合重载的要求,但是事实并非如此。

同时定义construct2和construct3,  以形式 A a;定义对象将会导致编译器报错。

error: call of overloaded ‘A()’
is ambiguous
       note: candidates
are: A::A(int, int)
      
note:               
              A::A(int)

以形式A a(1);定义对象将会导致报错:

error: call of
overloaded ‘A(int)’ is
ambiguous
       
note: candidates are: A::A(int,
int)
       
note:                
A::A(int)

note:                
A::A(const A&)

然而,以形式 A
a(1,2);定义对象则可以正确通过编译并运行。   这是什么原因?编译器工作的原理是什么?不知道....

5)
从重载的角度分析,construct2和construct3签名一致,construct4和construct5签名一致,不能同时定义。实验结果证实确实如此。

6) construct4和5恰为构造函数重载的典型实例,当然能正确编译运行。

7 )  只提供非默认构造函数,只有construct4或5或同时有4和5而无1/2/3

这将导致无法使用 A 
a;的形式来声明对象。因为以A a的形式声明对象是调用默认构造函数来构造对象的。编译器找不到默认构造函数自然会报错。

与此同时对象数组的声明A 
a[10];也将得到报错,原理同上。

那么如下声明并初始化对象数组会是什么结果呢?

A  a[3] = {

A(1,1),  A(2,2), A(3,3)

}  
//(再只定义了construct5的情况下)

在C++ Primer
Plus中有如下语句:“要创建类对象数组,则这个类必须有默认构造函数。”,“初始化对象数组的方案位,首先使用默认构造函数创建数组元素,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应的元素中。因此要创建类对象数组,则这个类必须有默认构造函数。”

在我看来这话不尽然正确,或许Stephen写书的时候使用的编译器是如他所说,但是我在g++中做的实现不是如此。上述对象数组的定义在g++下能够通过编译,并运行正确。g++的策略是,如果花括号中明确调用了构造函数来初始化对象,且该构造函数已被定义,那么将直接使用构造函数来创建对象元素,无临时对象和复制的过程。然而,如果花括号中有任一元素没有显示的使用已定义的构造函数初始化,那么编译器将调用默认构造函数来初始化该对象元素,这时如果没有定义默认构造函数,将无法通过编译。

那么在上述情形中   A  a[4]
= { A(1,1), A(2,2), A(3,3)},
将无法通过编译,因为A[3]实际上要通过调用默认构造函数来初始化,而这里又没有定义默认构造函数。

关于默认构造函数的调用。

实际上在g++中,未提供任何构造函数时,初始化一个对象,编译器并不去调用自动提供的那个什么也不做的构造函数,而仅仅是在内存中为对象分配了空间。(这应该是编译器的优化手段,不同编译器不同。)如果程序自己定义了一个A(){};虽然也什么都不做,但是初始化对象时,该构造函数却会被执行。这就带来了额外的开销,所以如果真的不定义任何构造函数的话,那么就干脆也别定义A(){};这么个空构造函数了,无意义,反而降低效率。

构造函数的参数传递,参数的第一个值为隐藏的this指针,指向对象的首地址,其次才是传进去的参数。

类对象的大小:无任何属性域的对象,大小为1, 即类class A{}; 
有属性域的类,大小为属性大小之和。this指针不占内存。

时间: 2024-10-19 21:17:42

c++类构造函数理解(转载)的相关文章

C#中Thread类中Join方法的理解(转载)

指在一线程里面调用另一线程join方法时,表示将本线程阻塞直至另一线程终止时再执行      比如 Java代码   using System; namespace TestThreadJoin { class Program { static void Main() { System.Threading.Thread x = new System.Threading.Thread(new System.Threading.ThreadStart(f1)); x.Start(); Console

关于C#中派生类调用基类构造函数的理解

(1)当基类中没有自己编写的构造函数时,派生类默认条用基类的构造函数 (2)当基类中有自己编写的构造函数时,要在基类中添加无参的构造函数 Java代码   public class MyBaseClass { public MyBaseClass() { } public MyBaseClass(int i) { Console.WriteLine("我是基类带一个参数的构造函数"); } } public class MyDerivedClass : MyBaseClass { pu

转 关于C#中派生类调用基类构造函数的理解

关于C#中派生类调用基类构造函数的理解 .c#class       本文中的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1.  当基类中没有自己编写构造函数时,派生类默认的调用基类的默认构造函数 Ex: public class MyBaseClass    {    } public class MyDerivedClass : MyBaseClass    {        public MyDerivedClass()        {            Con

对Lucene PhraseQuery的slop的理解[转载]

所谓PhraseQuery,就是通过短语来检索,比如我想查“big car”这个短语,那么如果待匹配的document的指定项里包含了"big car"这个短语,这个document就算匹配成功.可如果待匹配的句子里包含的是“big black car”,那么就无法匹配成功了,如果也想让这个匹配,就需要设定slop,先给出slop的概念:slop是指两个项的位置之间允许的最大间隔距离,下面我举例来解释: 我的待匹配的句子是:the quick brown fox jumped over

JavaScript es6 class类的理解。

在本篇文章我将会把我对JavaScript  es6新特性class类的理解.本着互联网的分享精神,我就将我自己的理解分享给大家. 使用es写一个类(构造函数) 在es5中大家一般都这么写一个类(构造函数) 另外需要注意,class类不会被提升. // 创建一个User构造函数 function User(name, age) { this.name = name; this.age = age; } // User构造函数的静态方法. User.getClassName = function

转:C++类构造函数初始化列表

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式.例如: class CExample { public:     int a;     float b;     //构造函数初始化列表     CExample(): a(0),b(8.8)     {}     //构造函数内部赋值     CExample()     {         a=0;         b=8.8;     } }; 上面的例子中两个构造函数的结果是一样

博弈论类题目小结——转载

出处http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 首先当然要献上一些非常好的学习资料: 基础博弈的小结:http://blog.csdn.net/acm_cxlove/article/details/7854530 经典翻硬币游戏小结:http://blog.csdn.net/acm_cxlove/article/details/7854534 经典的删边游戏小结:http://blog.csdn.net/acm

C++ 中类的构造函数理解(二)

C++ 中类的构造函数理解(二) 写在前面 上次的笔记中简要的探索了一下C++中类的构造函数的一些特性,这篇笔记将做进一步的探索.主要是复制构造函数的使用. 复制构造函数 复制构造函数也称拷贝构造函数,它只有单个形参,且该形参是对本类类型对象的引用.其作用有以下几点: 1.根据另一个同类型的对象显示或隐式初始化一个对象 2.复制一个对象,将它作为实参传递给一个函数 3.从函数返回时复制一个对象 4.初始化顺序容器中的元素 5.根据元素初始化列表初始化数组元素 编译器合成的复制构造函数 如同默认构

派生类构造函数和析构函数的构造规则

1.当基类的构造函数没有参数,或没有显示定义构造函数时,派生类可以不向基类传递参数,甚至可以不定义构造函数. 2.派生类不能继承基类的构造函数和析构函数. 3.当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径.转载自:http://blog.sina.com.cn/s/blog_3c6889fe0100t4cq.html