首页 > 代码库 > 构造函数

构造函数

概述

为了类的使用方便,类的设计尽可能使类的使用方式与内置类型无区别,构造函数则是类模仿内置类型的初始化
内置类型初始化支持直接初始化,复制初始化,默认初始化,构造函数的各种类型对应于内置类型的各种初始化:
  • 带参数构造函数——直接初始化
  • 复制构造函数——复制初始化
  • 默认构造函数——默认初始化
构造函数特点:
  • 构造函数函数名与类名同名,用来标记和识别构造函数
  • 构造函数没有返回值,构造函数是对自身的初始化,因此返回值没有任何意义,因为没有返回值的接收者
  • 构造函数是初始化行为,生成对象时自动调用,因此一个对象的构造函数只能执行一次,否则就会多次初始化
构造函数组成:
  • 构造函数是初始化行为,构造函数其实分两部分,初始化列表和构造函数函数体,真正完成初始化行为的是初始化列表
  • 初始化列表确保类的所有数据成员都得到适当的初始化
  • 构造函数函数体与普通函数函数体并无区别,构造函数的函数体可能进行一些深层的定制,一般完成的都是一些简单功能

辅助类

class CAnimal
{
public:
    CAnimal() : mGroup(0)
    {
        cout << "CAnimal()" << endl;
    }
    
    CAnimal(int group) : mGroup(group)
    {
        cout << "CAnimal(" << group << ")" << endl;
    }
    
    CAnimal(const CAnimal &other) : mGroup(other.mGroup)
    {
        cout << "CAnimal(const CAnimal &other)" << endl;
    }
    
public:
    void info()
    {
        cout << "CAnimal.mGroup = " << mGroup << endl;
    }
    
private:
    int mGroup;
};

class CDog : public CAnimal
{
public:
    CDog()
    {
        cout << "CDog()" << endl;
    }
    
    CDog(int price) : CAnimal(1), mPrice(price)
    {
        cout << "CDog(" << mPrice << ")" << endl;
    }
    
    CDog(const CDog &other) : CAnimal(other), mPrice(other.mPrice)
    {
        cout << "CDog(const CDog &other)" << endl;
    }
    
public:
    void info()
    {
        CAnimal::info();
        cout << "CDog.mPrice = " << mPrice << endl;
    }
    
private:
    int mPrice;
};

class CCat : public CAnimal
{
public:
    CCat()
    {
        cout << "CCat()" << endl;
    }
    
    CCat(int price) : CAnimal(2), mPrice(price)
    {
        cout << "CCat(" << mPrice << ")" << endl;
    }
    
    CCat(const CCat &other) : CAnimal(other), mPrice(other.mPrice)
    {
        cout << "CCat(const CCat &other)" << endl;
    }
    
public:
    void info()
    {
        CAnimal::info();
        cout << "CCat.mPrice = " << mPrice << endl;
    }
    
private:
    int mPrice;
};

class CPig : public CAnimal
{
public:
    CPig()
    {
        cout << "CPig()" << endl;
    }
    
    CPig(int price) : CAnimal(3), mPrice(price)
    {
        cout << "CPig(" << mPrice << ")" << endl;
    }
    
    CPig(const CPig &other) : CAnimal(other), mPrice(other.mPrice)
    {
        cout << "CPig(const CPig &other)" << endl;
    }
    
public:
    void info()
    {
        CAnimal::info();
        cout << "CPig.mPrice = " << mPrice << endl;
    }
    
private:
    int mPrice;
};

class CDonkey : public CAnimal
{
public:
    CDonkey()
    {
        cout << "CDonkey()" << endl;
    }
    
    CDonkey(int price) : CAnimal(4), mPrice(price)
    {
        cout << "CDonkey(" << mPrice << ")" << endl;
    }
    
    CDonkey(const CDonkey &other) : CAnimal(other), mPrice(other.mPrice)
    {
        cout << "CDonkey(const CDonkey &other)" << endl;
    }
    
public:
    void info()
    {
        CAnimal::info();
        cout << "CDonkey.mPrice = " << mPrice << endl;
    }
    
private:
    int mPrice;
};

class CHorse : public CAnimal
{
public:
    CHorse()
    {
        cout << "CHorse()" << endl;
    }
    
    CHorse(int price) : CAnimal(5), mPrice(price)
    {
        cout << "CHorse(" << mPrice << ")" << endl;
    }
    
    CHorse(const CHorse &other) : CAnimal(other), mPrice(other.mPrice)
    {
        cout << "CHorse(const CHorse &other)" << endl;
    }
    
public:
    void info()
    {
        CAnimal::info();
        cout << "CHorse.mPrice = " << mPrice << endl;
    }
    
private:
    int mPrice;
};

初始化顺序

class CFarmLand
{
public:
    CFarmLand()
    {
        cout << "CFarmLand()" << endl;
    }
    
    CFarmLand(int area) : mArea(area)
    {
        cout << "CFarmLand(" << mArea << ")" << endl;
    }
    
    CFarmLand(const CFarmLand &other) : mArea(other.mArea)
    {
        cout << "CFarmLand(const CFarmLand &other)" << endl;
    }
    
public:
    void info()
    {
        cout << "mArea = " << mArea << endl;
    }
    
public:
    int mArea;
};

class CFarm : public CFarmLand
{
public:
    CFarm()
    {
        cout << "CFarm()" << endl;
    }
    
    CFarm(int kind) : mKind(kind), mDog(300), mCat(100), mPig(500), CFarmLand(800)
    {
        cout << "CFarm(" << mKind << ")" << endl;
    }
    
    CFarm(const CFarm &other) : mKind(other.mKind), CFarmLand(other)
    {
        cout << "CFarm(const CFarm &other)" << endl;
    }
    
public:
    void info()
    {
        CFarmLand::info();
        cout << "mKind = " << mKind << endl;
        mPig.info();
        mDog.info();
        mCat.info();
    }
    
private:
    int mKind;
    
private:
    CPig mPig;
    CDog mDog;
    CCat mCat;
};
void construct()
{
    {
        cout << "-----1-start-----" << endl;
        cout << "-----construct-----" << endl;
        CFarm farm;
        cout << "-----info-----" << endl;
        farm.info();
        cout << "-----1-end-----" << endl;
    }
    
    {
        cout << "-----2-start-----" << endl;
        cout << "-----construct-----" << endl;
        CFarm farm(3);
        cout << "-----info-----" << endl;
        farm.info();
        cout << "-----2-end-----" << endl;
    }
}
output:
-----1-start-----
-----construct-----
CFarmLand()
CAnimal()
CPig()
CAnimal()
CDog()
CAnimal()
CCat()
CFarm()
-----info-----
mArea = 0
mKind = 0
CAnimal.mGroup = 0
CPig.mPrice = 0
CAnimal.mGroup = 0
CDog.mPrice = -587201213
CAnimal.mGroup = 0
CCat.mPrice = 32767
-----1-end-----
-----2-start-----
-----construct-----
CFarmLand(800)
CAnimal(3)
CPig(500)
CAnimal(1)
CDog(300)
CAnimal(2)
CCat(100)
CFarm(3)
-----info-----
mArea = 800
mKind = 3
CAnimal.mGroup = 3
CPig.mPrice = 500
CAnimal.mGroup = 1
CDog.mPrice = 300
CAnimal.mGroup = 2
CCat.mPrice = 100
-----2-end-----
结论:
  • 数据成员分两类:内置类型数据成员,类类型数据成员
  • 初始化列表可以显式初始化类的基类和数据成员,也可以隐式初始化类的基类和数据成员
  • 显式初始化——基类显式调用合适的构造函数,类类型数据成员显式调用合适的构造函数,内置类型数据成员显式指定初始化值
  • 隐式初始化——基类隐式调用默认构造函数,类类型数据成员隐式调用默认构造函数,内置类型数据成员隐式初始化
  • c++在性能和安全之间选择追求性能,因此对于内置类型数据成员的隐式初始化就是什么也不做的初始化,因此隐式初始化的内置类型数据成员没有值
  • 数据成员初始化顺序与初始化列表中初始化式顺序无关,与数据成员在类定义中的顺序一致
  • 初始化列表确保类的每个部分(基类,数据成员)都得到合适的初始化,因此初始化列表中基类和类类型数据成员都必须调用合适的构造函数,如果调用默认构造函数,可以显式调用,也可以隐式调用(省略不写)
  • 类的数据成员和基类初始化只与初始化列表有关,与构造函数函数体无关
  • 进入构造函数函数体前,类的所有成员必须得到合适的初始化,c++认为访问类的未初始化成员是不安全的,尽管内置类型数据成员的隐式初始化什么也不做,但是c++认为也完成了初始化
  • 继承类的初始化列表和构造函数函数体可引用基类数据成员(只要访问权限允许),但是不可在继承类初始化列表中再次初始化基类数据成员,否则基类数据成员会多次初始化(基类初始化列表中已初始化,继承类初始化列表中再次初始化)
  • 类继承体系中初始化列表和构造函数函数体调用顺序为:基类初始化(基类初始化列表->基类构造函数函数体)->继承类(继承类初始化列表->继承类构造函数函数体)

默认构造函数

误解——默认构造函数是无参构造函数
正解——默认构造函数是无需传递参数就可调用的构造函数,包括无参构造函数和有参但所有参数含默认实参值的构造函数
class CAnimal
{
public:
    CAnimal()
    {
        cout << "CAnimal()" << endl;
    }

    CAnimal(int group = 0) : mGroup(group)
    {
        cout << "CAnimal(" << group << ")" << endl;
    }

    CAnimal(int group = 0, int subGroup = 1) : mGroup(group), mSubGroup(subGroup)
    {
        cout << "CAnimal(" << group << ", " << subGroup << ")" << endl;
    }

private:
    int mGroup;
    int mSubGroup;
};

合成默认构造函数

如果没有显式声明构造函数,编译器会合成默认构造函数,一旦显式声明了构造函数(即使没有默认构造函数),编译器不再合成默认构造函数,编译器认为开发者知道怎么构造对象,不再需要编译器的帮助
合成默认构造函数的初始化列表中会调用基类默认构造函数,类类型数据成员的默认构造函数,内置类型数据成员的隐式初始化,确保类的每个部分都得到合适的初始化,合成默认构造函数函数体为空
class CAdvanceFarm : public CFarm
{
private:
    CHorse mHorse;
    CDonkey mDonkey;
};
CAdvanceFarm没有显式声明构造函数,就会合成默认构造函数,合成的默认构造函数为:
CAdvanceFarm() : CFarm(), mHorse(), mDonkey() {}
void construct()
{
    cout << "-----construct-----" << endl;
    CAdvanceFarm farm;
}
output:
-----construct-----
CFarmLand()
CAnimal()
CPig()
CAnimal()
CDog()
CAnimal()
CCat()
CFarm()
CAnimal()
CHorse()
CAnimal()
CDonkey()
注:对于合成默认构造函数,必须确保基类和类类型数据成员能调用其对应的默认构造函数,否则编译错误,因为不符合“c++的构造函数(初始化列表)确保类的所有数据成员(基类和数据成员)都得到合适的初始化”原则

复制构造函数

复制构造函数是构造函数的一个特殊版本,形参列表为[const] classname &,当类对象进行复制初始化时调用
void copyConstruct()
{
    {
        cout << "-----1-start-----" << endl;
        cout << "-----construct-----" << endl;
        CFarm farm(3);
        cout << "-----info-----" << endl;
        farm.info();
        cout << "-----copy construct-----" << endl;
        CFarm copyFarm = farm;
        cout << "-----info-----" << endl;
        copyFarm.info();
        cout << "-----1-end-----" << endl;
    }
}
output:
-----1-start-----
-----construct-----
CFarmLand(800)
CAnimal(3)
CPig(500)
CAnimal(1)
CDog(300)
CAnimal(2)
CCat(100)
CFarm(3)
-----info-----
mArea = 800
mKind = 3
CAnimal.mGroup = 3
CPig.mPrice = 500
CAnimal.mGroup = 1
CDog.mPrice = 300
CAnimal.mGroup = 2
CCat.mPrice = 100
-----copy construct-----
CFarmLand(const CFarmLand &other)
CAnimal()
CPig()
CAnimal()
CDog()
CAnimal()
CCat()
CFarm(const CFarm &other)
-----info-----
mArea = 800
mKind = 3
CAnimal.mGroup = 0
CPig.mPrice = 0
CAnimal.mGroup = 0
CDog.mPrice = 855688896
CAnimal.mGroup = 0
CCat.mPrice = 32767
-----1-end-----
类对象复制初始化时调用复制构造函数,复制构造函数与普通构造函数没有本质区别,只是其形参比较特殊而已

合成复制构造函数

如果没有显式声明复制构造函数,编译器会合成复制构造函数,一旦显式声明了复制构造函数,编译器不再合成复制构造函数,编译器认为开发者知道怎么复制对象,不再需要编译器的帮助
合成复制构造函数形参列表为const classname &,初始化列表中会调用基类复制构造函数,类类型数据成员的复制构造函数,内置类型数据成员的复制初始化,确保类的每个部分都得到合适的初始化,合成复制构造函数函数体为空
class CAdvanceFarm : public CFarm
{
private:
    CHorse mHorse;
    CDonkey mDonkey;
};
CAdvanceFarm没有显式声明复制构造函数,就会合成复制构造函数,合成的复制构造函数为:
CAdvanceFarm(const CAdvanceFarm &other) : CFarm(other), mHorse(other.mHorse), mDonkey(other.mDonkey) {}
void copyConstruct()
{
    cout << "-----construct-----" << endl;
    CAdvanceFarm farm;
    cout << "-----copy construct-----" << endl;
    CAdvanceFarm copyFarm = farm;
}
output:
-----construct-----
CFarmLand()
CAnimal()
CPig()
CAnimal()
CDog()
CAnimal()
CCat()
CFarm()
CAnimal()
CHorse()
CAnimal()
CDonkey()
-----copy construct-----
CFarmLand(const CFarmLand &other)
CAnimal()
CPig()
CAnimal()
CDog()
CAnimal()
CCat()
CFarm(const CFarm &other)
CAnimal(const CAnimal &other)
CHorse(const CHorse &other)
CAnimal(const CAnimal &other)
CDonkey(const CDonkey &other)
注:对于合成复制构造函数,必须确保基类和类类型数据成员能调用其对应的复制构造函数,否则编译错误,因为不符合“c++的构造函数(初始化列表)确保类的所有数据成员(基类和数据成员)都得到合适的初始化”原则

复制构造函数形参的const修饰

class CAnimal
{
public:
    CAnimal() : mGroup(0)
    {
        cout << "CAnimal()" << endl;
    }
    
    CAnimal(int group) : mGroup(group)
    {
        cout << "CAnimal(" << group << ")" << endl;
    }
    
    CAnimal(CAnimal &other) : mGroup(other.mGroup)
    {
        cout << "CAnimal(const CAnimal &other)" << endl;
    }
    
public:
    void info() const
    {
        cout << "CAnimal.mGroup = " << mGroup << endl;
    }
    
private:
    int mGroup;
};
void copyConstruct()
{
    {
        cout << "-----1-start-----" << endl;
        cout << "-----construct-----" << endl;
        const CAnimal animal(1);
        cout << "-----info-----" << endl;
        animal.info();
        cout << "-----copy construct-----" << endl;
        //CAnimal copyAnimal = animal;
        cout << "-----1-end-----" << endl;
    }
}
自定义复制构造函数的形参不带const修饰时,编译器不再合成const版本的复制构造函数,编译器认为开发者知道怎么复制对象,不再需要编译器的帮助
复制构造函数总结:
  • 复制构造函数与普通构造函数无区别,只不过形参列表为[const] classname &
  • 合成复制构造函数的形参列表为const classname &
  • 合成复制构造函数的初始化列表中会调用基类复制构造函数,类类型数据成员的复制构造函数,内置类型数据成员的复制初始化
  • 显式复制构造函数如果形参列表声明为classname &,编译器不会再合成复制构造函数,编译器认为开发者知道怎么复制对象,不再需要编译器的帮助

构造函数