EffectC++(二):构造/析构/赋值运算

条款5:了解C++默默为你编写的三个函数

注意点1:三个函数都是public且inline 且non-virtual.

注意点2:Copy 构造函数 和 CopyAssignment 操作符 会将源对象的每一个非non-static 拷贝的目标对象。

例子:

#include<iostream>
class CTestA
{
  public:
    void SetValue(int a,int *b)
    {
        i = a;
        j = b;
    }
    void Show()
    {
        std::cout<<"i = "<<i<<std::endl;
        std::cout<<"j = "<<j<<std::endl;
    }
 
  private:
    int i;
    int *j;
    static int k;
};
 
int main()
{
    CTestA a,b;//采用默认构造函数
    std::cout<<"defalut a:"<<std::endl;
    a.Show();
    std::cout<<"defalut b:"<<std::endl;
    b.Show();
    int i = 0;
    int *j = &i;
    a.SetValue(i,j);
    b = a;
    std::cout<<"SetValue a:"<<std::endl;
    a.Show();
    std::cout<<"Copy assignmentb:"<<std::endl;
    b.Show();
    CTestA c(a);
    std::cout<<"Copy c:"<<std::endl;
    c.Show();
    return 0;
}

结果:


 

条款6:如果不想使用系统默认的这三个默认函数,需要明确的拒绝。

拒绝的方法:将需要拒绝的函数放置到private区域。

如:

#include<iostream>
class CTestA
{
  public:
    void SetValue(int a,int *b)
    {
        i = a;
        j = b;
    }
    void Show()
    {
        std::cout<<"i ="<<i<<std::endl;
        std::cout<<"j ="<<j<<std::endl;
    }
 
 private:// 这里
    CTestA(const CTestA &other);
    CTestA & operator=(const CTestA&other);
 
 
  private:
    int i;
    int *j;
    static int k;
};


 

条款7:为多态基类声明virtual析构函数

注意点1:如果该类不会作为基类(也就是被其他继承)请不要使用virtual析构函数。另外,请不要继承使用non-virtual析构。

比如:classCTestA:publicstd::string

如果你使用的一个指针指向它,在析构的时候将会步入未定义行为的恶地。

 

技巧点:使用pure析构函数 (纯虚函数)

例:注意到即使使用pure析构函数同样可以再定义。

#include<iostream>
#include<string>
classCTestBase
{
  public:
    virtual ~CTestBase() = 0;
    void SetValue(int a,int *b)
    {
        i = a;
        j = b;
    }
    void Show()
    {
        std::cout<<"i ="<<i<<std::endl;
        std::cout<<"j ="<<j<<std::endl;
    }
 
  private:
    int i;
    int *j;
};
 
CTestBase::~CTestBase()
{
    std::cout<<"this is~Base"<<std::endl;
}
 
classCTestDerived:public CTestBase
{
public:
    virtual ~CTestDerived();
};
 
CTestDerived::~CTestDerived()
{
    std::cout<<"this is~Derived"<<std::endl;
}
 
int main()
{
    CTestDerived a;
    return 0;
}


 

条款8:严禁析构出现异常

注:此处不是谈论异常的处理,而是谈论如何在析构的时候避开异常提示。

此处列举的例子具有具体性,假设一个数据库类,在析构的时候需要断开此连接

例子:

1.      一般不会采用的做法:

classCTestDataBase
{
public:
    CTestDataBase()
    {
       std::cout<<"ConnectDB!"<<std::endl;
    }
    ~CTestDataBase()
    {
        std::cout<<"CloseDB!"<<std::endl;
    }
};


2.      比较好的做法:

#include<stdexcept>
classCTestDataBase
{
public:
    CTestDataBase()
    {
        ConnectDB();
    }
    ~CTestDataBase()
    {
        if(!m_bClosed)// 双保险2
        {
            try
            {
                CloseDB();
            }
            catch(...)
            {
                std::cout<<"CloseDBerror"<<std::endl;
            }
        }
    }
    void ConnectDB()
    {
        std::cout<<"ConnectDB!"<<std::endl;
        m_bClosed = false;
    }
    void CloseDB()//双保险1
    {
       std::cout<<"CloseDB!"<<std::endl;
       m_bClosed = true;
    }
 
private:
    bool m_bClosed;
};
int main()
{
    CTestDataBase a;
    return 0;
}


 

条款9:绝不在构造函数和析构函数中调用virtual函数

注意点:逻辑本身不建议这么做。

例如:

#include <iostream>
#include <string>
#include <stdexcept>
class CBase
{
public:
   CBase()
    {
      Log();
    }
   virtual void Log()
    {
       std::cout<<"Base Log"<<std::endl;
    }
};
class CDerived:public CBase
{
public:
   CDerived():CBase()
    {
       Log();
    }
   virtual void Log()
    {
       std::cout<<"Derived Log"<<std::endl;
    }
};
int main()
{
   CDerived a;
   return 0;
}


执行结果:


如此运行了两次,如果非必要的话,请谨慎使用。

 

条款10:令operator= 返回一个reference to *this

注:此处的operator=泛指+=/ -= / *= / /= ..等等

原因:连续赋值。

#include <iostream>
#include <string>
#include <stdexcept>
class CTest
{
public:
    void SetValue(int v)
    {
        val = v;
    }
    int GetValue() const
    {
        return val;
    }
    void ShowValue()
    {
        std::cout<<"val = "<<val<<std::endl;
    }
    CTest& operator=(const /*小心这里的const 只能调用const函数*/ CTest& other)
    {
        this->SetValue(other.GetValue());
        return *this;
    }
 
private:
    int val;
};
 
int main()
{
    CTest a,b,c;
    a.SetValue(1);
    b = c = a;
    std::cout<<"a:";
    a.ShowValue();
    std::cout<<"b:";
    b.ShowValue();
    std::cout<<"c:";
    c.ShowValue();
    return 0;
}

条款11:小心在operator=中对自己赋值

处理办法1:对自我赋值进行检测

    CTest& operator=(const CTest& other)
    {
        if(this == other)
            return *this;
        this->SetValue(other.GetValue());
        return *this;
}


处理办法2(增加了对出现异常的处理):
注:两种书写方式本质上是一样的,swap不必再区别2个对象是否还会存在不同。
    //书写方式1
    CTest& operator=(const CTest& other)
    {
        CTest other2(other); // 多构建一个副本
        swap(other2);
        return *this;
    }
    //书写方式2
    CTest& operator=(CTest other)
    {
       swap(other);
       return *this; 
    }


 

条款12:复制对象时请不要忘记任何一个成分

注意点:

这是个有意思的注意点:不要在派生类中的copyassignment 调用 copy 构造函数。因为那样的结果是不确定的,除非你自己很确定。


后记:三个默认函数就像三剑客一般,一般你需要重新定义其中一个,你就需要另外两个。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值