C++语法系列之10--构造函数总结

1 不提供显示的构造函数

class Counter {
  private:
     int cnt;
 public getCnt() {
     return cnt;
 }
  
}

Counter myCnt;//在堆栈中,此时会自动调用默认构造函数初始化myCnt
myCnt.getCnt();//0

此时编译器会默认为类生成一个默认构造函数(不带参数的构造函数,什么也不做)。上例中打印结果为0(类中定义的成员变量,基本类型会初始化为0)。
编译器提供的默认构造函数相当于:

Counter() {
  //空实现
}

2 显示的提供构造函数

class Counter {
private:
    int mA;
    float mB;
public:
    Counter(int a);
    Counter(int a, float b);
    
};
Counter::Counter(int a) {
    mA = a;
}
Counter::Counter(int a, float b) {
    mA = a;
    mB = b;
}

int main(int argc, const char * argv[]) {
    //Counter cnt;//error, cannot compile
    Counter myCnt(1);
    Counter myCnt2(1,3.0);

}

上面的例子提供了2个构造函数(也即重载构造函数)。

3 复制构造函数

复制构造函数的参数为对象的引用


class Counter {
private:
    int mA;
    float mB;
public:
    Counter(int a);
    Counter(int a, float b);
    Counter(const Counter& c);
    
    int getMA() const;
};
Counter::Counter(int a) {
    mA = a;
}
Counter::Counter(int a, float b) {
    mA = a;
    mB = b;
}

Counter::Counter(const Counter& c) {
    mA = c.mA;
    mB = c.mB;
}

int Counter::getMA() const {
    return mA;
}

int main(int argc, const char * argv[]) {
    Counter myCnt(1,3.0);
    Counter myCopyCnt(myCnt);
    cout << myCopyCnt.getMA() << endl;
}

注意:

  1. 复制构造函数只在初始化的时候才会调用。如果是赋值而不是初始化,那么调用的是赋值运算符,而不是复制构造函数。
  2. 浅拷贝/深拷贝:复制构造函数和赋值运算符,如果有指针数据成员,涉及到内存分类的场景的话,需要考虑浅拷贝/深拷贝。浅拷贝只是将引用或者指针指向对应的成员变量。而深拷贝则会分配内存,然后将引用指向对应的内存。

4 类型转换构造函数


class Complex {
private:
    float mReal;
    float mImg;
public:
    Complex();
    Complex(float real);
    Complex(const Complex& c);
    
    Complex operator+(const Complex& c);

    float getReal() const;
    float getImg() const;
    void setReal(float real);
    void setImg(float img);
};

Complex::Complex() {
    mReal = 0.0;
    mImg = 0.0;
}

Complex::Complex(const Complex& c) {
    mReal = c.mReal;
    mImg = c.mImg;
}

Complex::Complex(float real) {
    mReal = real;
    mImg = 0;
}

float Complex::getReal() const {
    return mReal;
}

float Complex::getImg() const {
    return mImg;
}

Complex Complex::operator+(const Complex& c) {
    Complex complex;
    complex.setReal(mReal + c.getReal());
    complex.setImg(mImg + c.getImg());
    return complex;
}

int main(int argc, const char * argv[]) {
    Complex complex;
    Complex c1 = 1.0f;
    cout << "real:" << c1.getReal() << ";img:" << c1.getImg() << endl;
   # real: 1.0; img:0.0

  Complex a(2.0);
  Complex b = a + 1.0;//real: 3.0
}
  1. 上面的例子中,Complex c1= 1.0f; 会调用Complex的构造函数Complex(float real),也就是类型转换.
  2. Complex b = a + 1.0 此时提供了operator+,也会隐式的调用构造函数进行类型转换。但是Complex c = 1.0 + a;无法编译通过,因为Complex的operator+运算符不具有交换性,如果要可以编译通过,必须提供全局的operator+ 才可以。

5 禁止隐式调用构造函数进行类型转换

有些时候,上面的效果是我们期望的。但是有些时候,如果不是我们需要的(比如错误的写了Complex c1 = 1.0f),如何禁止这种类型转换呢?
可以使用explicit关键字解决:

class Complex {
private:
    float mReal;
    float mImg;
public:
    Complex();
    explicit Complex(float a);
    Complex(const Complex& c);
    
    Complex operator+(const Complex& c);

    float getReal() const;
    float getImg() const;
    void setReal(float real);
    void setImg(float img);
};

Complex::Complex() {
    mReal = 0.0;
    mImg = 0.0;
}

Complex::Complex(const Complex& c) {
    mReal = c.mReal;
    mImg = c.mImg;
}

Complex::Complex(float a) {
    mReal = a;
    mImg = 0;
}

float Complex::getReal() const {
    return mReal;
}

float Complex::getImg() const {
    return mImg;
}

void Complex::setImg(float img) {
    mImg = img;
}

void Complex::setReal(float real) {
    mReal = real;
}

int main(int argc, const char * argv[]) {
    Complex complex;
    Complex c1 = 1.0f; //error,此时无法将float类型转换为Complex
    Complex c2(1.0f);   //此时必须这样显示的调用构造函数

注意:
1)explicit用于用于只有一个参数的构造函数才有效;
2)假如构造函数有两个参数,explicit无效;
3)假如构造函数有2个或者多个参数,但是除了第一个参数外,其余均带有默认值时,explicit有效;

6 赋值运算符

//为了支持连等操作(形如这样: a =b  =c ),返回值必须为引用
Complex &operator=(const Complex &rhs)
   {
       if ( this == &rhs ) 
       {
           return *this;
       }
            
       // 复制等号右边的成员到左边的对象中
       this->mReal = rhs.mReal;
       this->mImg = rhs.mImg;
       return *this;
   }

以上是一个赋值运算符的例子,没有涉及到内存分配。所以看不出来深拷贝和浅拷贝的区别。如果有引用和指针,涉及到内存分配,那么需要使用深拷贝。

7 带有默认值的构造函数

class Base {
public:
    Base();
    Base(int a = 0);
    
private:
    int mA;
};
Base::Base() {
}

Base::Base(int a) {
    mA = a;
}

int main(int argc, const char * argv[]) {
    Base b;//Error:  Call to constructor of "Base" is ambiguous
    return 0;
}

以上代码定义了两个构造函数:无参构造函数和带有默认值得构造函数。由于无法这两个构造函数,编译会出错。
所以:定义构造函数时,不允许定义导致歧义的构造函数。

8 继承层次的构造函数默认值


class Base {
public:
    Base(int a = 0);
    virtual ~Base();
    
private:
    int mA;
};


Base::Base(int a) {
    mA = a;
    cout << "Base a = " << mA << endl;
}

Base::~Base() {
    
}

class Derived : public Base {
public:
    Derived(int a = 1);
    virtual ~Derived();
};

Derived::Derived(int a) {
    cout <<"Derived a = " << a << endl;
}

Derived::~Derived() {
    
}

int main(int argc, const char * argv[]) {
    Derived d;
    cout << endl << endl;
    Base* b = new Derived();
    
    return 0;
}

运行结果如下:

Base a = 0
Derived a = 1


Base a = 0
Derived a = 1

说明:C++中默认参数不会继承。C++根据描述对象的表达式类型绑定默认参数,而不是根据实际的对象类型绑定参数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 228,546评论 6 533
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,570评论 3 418
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 176,505评论 0 376
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,017评论 1 313
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,786评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,219评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,287评论 3 441
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,438评论 0 288
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,971评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,796评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,995评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,540评论 5 359
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,230评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,662评论 0 26
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,918评论 1 286
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,697评论 3 392
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,991评论 2 374

推荐阅读更多精彩内容