拷贝函数的正常使用

C++面向对象的程序语言,到处存在继承体系:整理一下使用构造函数与析构函数注意事项:

 

Item1: 编译器可以隐式自动生成的函数包括四个:

defatult constructor:默认构造函数

copy constructor:拷贝构造函数

copy assignment operator:拷贝赋值函数

destructor:析构函数

编译器可以自动生成,并不代表一定会生成,只有在用户未手动声明对应函数时,编译器可以自动生成相应的函数。

classBase{};  // 四个编译器全部自动生成

classBase

{

        Base();

};             // 编译器生成除default constructor外的三个函数

 

Item2: 不想使用编译器自动生成的函数应该明确告诉编译器

       如果想要实现object禁止拷贝功能:

classBase    // F1

{

public:

    Base(){}

    ~Base(){}

private:

    Base(constBase&);

    Base&operator=(constBase&);

};

 

 

classBase    // F2

{

public:

    Base(){}

    ~Base(){}

    Base(constBase&)=delete;

    Base&operator=(constBase&)=delete;

};

 

 

classBase    // F3

{

public:

    Base(){}

    ~Base(){}

private:

    Base(constBase&)=delete;

    Base&operator=(constBase&)=delete;

}

 

Q1:为什么拷贝函数设置为私有函数?

A1:因为私有函数外部无法访问,可以达到无法创建object的目的。

 

Q2:为什么拷贝函数只有声明而没有实现?

A2:因为万事都有例外,友元函数可以冲破private,所以只有声明没有实现,

从而可以达到调用拷贝函数提醒报错的目的。

 

Q3:除了私有化拷贝函数还有别的方法实现吗?

A3:有,利用C++11的特性,使用keyword:delete。

 

 

Item3: member data initializtion of object(member initialization list)

       Baseclass的初始化要在Derived class前初始化,成员变量的初始化要在初始化列表中按照声明顺序进行初始化。


 

       因为只调用一次copy constructor function效率比先调用一次defaultconstructor function后再调用一次copy assignment operator效率要高的多。所有建议使用member initializtion list对member data 进行初始化。    

classPeople
{
public:
    People(conststd::stringname,constintage);
    virtual~People();
private:
    std::stringm_str_name;
    intm_int_age;
};
People::People(conststd::stringname,constintage)
    :m_str_name(name),m_int_age(age){}
People::~People(){}
 
classHoney:publicPeople
{
public:
    Honey(conststd::stringname,constintage);
    ~Honey();
};
 
Honey::Honey(conststd::stringname,constintage)
    :People(name,age){}
Honey::~Honey(){}

 

Item4: virtual destructor的正确使用

       如果一个class准备作为base class 使用,将destructor设置为virtual destructor是一个受欢迎的行为。因为在创建derived class时候,可以自动销毁base class资源。

       如果一个class不准备作为base class使用,将destructor设置为virtual destructor是一不好的想法。因为虚函数意味着使用更多的内存。

 

Item5: copy all data(local data and base data)

       拷贝一个对象的所有数据包括:本地所有数据成员,以及调用基类中适当的拷贝函数。

       如果为一个类增加了一个成员,则就应该更新所有拷贝函数。确保所有数据成员都可以正确拷贝。

       不要企图用一个拷贝函数调用另一个拷贝函数完成相应的功能。重复的代码可以提取出来作为第三个函数(private:init();)

   

classPeople

{

public:

    People()=default;

    virtual~People(){}

};

 

classHoney:publicPeople

{

public:

    Honey(conststd::stringname,constintage);

    ~Honey();

private:

    std::stringm_str_name;

    intm_int_age;   

};

 

Honey::Honey(conststd::stringname,constintage)

    :People(),m_str_name(name),m_int_age(age){}

Honey::~Honey(){}

 

Item6: construtors and destrcutors不应该调用virtual function

       虚函数的实现依赖于vptr,vptr存在于base class 中。

不要在constructor和destructor中调用virtual function,因为不会出现多态现象,为什么?因为在constructor中调用virtual function时,derived object还没有创建完成,只会出现base class中的行为。在析构函数中调用virtual function当寻找vptr时derived class已经被析构完成,则仍为base class中的行为。

如果拷贝函数与构造函数存在重复的代码可以将公共的代码提取出来放入第三个函数(private function)。

classPeople
{
public:
    People(){init("people",30);}
    virtual~People(){}
private:
    virtualvoidinit(std::stringname,constintage)=0;
    virtualvoiddestroy()=0;
};
 
classHoney:publicPeople
{
public:
    Honey(){init("honey",28);}
    ~Honey();
private:
    std::stringm_str_name;
    intm_int_age;
    voidinit(std::stringname,constintage)override;
    voiddestroy()override;
};
 
voidHoney::init(std::stringname,constintage)
{
    m_str_name=name;
    m_int_age=age;
}
voidHoney::destroy(){}

 

Item7: exceptions不应该出现有constructor中

对于异常的结果处理分为三种:

第一种:抑制异常使用程序继续运行。

第二种:终止程序。

第三种:提供常规异常处理函数(需要用户参与时)。

       destructor应该永不引发异常,因为如果在destructor中引发异常意味着内存泄漏。如果调用可能会发生异常的函数,则destructor应该捕获所有异常,然后抑制异常或者终止程序。一般来说,抑制异常比程序提前终止或产生未定义行为更可取。因为一个应用程序必须可以保证抑制异常后还能正常可靠的运行。

       建议使用智能指针代替常规指针,使用vector代替数组。

 

Item8: assignment operator should return refernce to*this

assignment operator是一个右结合运算符

int x, y, z;

x = (y = (z = 15));

classPeople

{

public:

    People()=default;

    People&operator=(constPeople&rhs);

private:

    std::stringm_str_name;

};

 

People&People::operator=(constPeople&rhs)

{

    m_str_name=rhs.m_str_name;

    return*this;   

}

 

 

Item9:在asiignment中处理assignment to self

assignment to self是由于别名造成的(多个指针指向同一块内存,多个引用与同一个变量进行绑定)。

classPeople

{

public:

    People()=default;

    People&operator=(constPeople&rhs);

private:

    std::stringm_str_name;

};

 

People&People::operator=(constPeople&rhs)

{

    if(this==&rhs)     return*this;  //自赋值安全

    People*p=this;                   //异常安全

    this=newPeople(rhs);

deletep;

 

    return*this;   

}

 

如果可以做到上面九点,我想应该可以安全高效的处理好对象之间的拷贝关系了。

参考effective c++后的个人意见,仅供参考,如果有什么忽略的或者好的意见,请大家不吝赐教。

(ps :多年工作以来,一直都看别人的文章,以后本人会整理更多想法与大家分享:大家可以一直讨论,一起学习,一直进步。邮箱:wangqing10520@sina.com)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值