欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Effective C++函数编译器

程序员文章站 2022-06-24 20:22:02
P5:了解C++默默编写并调用哪些函数 1:编译器可以暗自为class创建 default构造函数、copy构造函数、copy assignment操作符、析构函数。 唯有这些函...
P5:了解C++默默编写并调用哪些函数

1:编译器可以暗自为class创建 default构造函数、copy构造函数、copy assignment操作符、析构函数。 唯有这些函数被需要调用,他们才会被编译器创建出来。

2:如果你打算在一个“内含引用成员”或者“内含const成员”的class内支持赋值操作,你必须自己定义copy assignment操作符。

3:如果base classes将copy assignment操作符声明为private,编译器将拒绝为子类生成copy assignment操作符。

4:编译器产生的析构函数是个non-virtual函数。

P6:若不想使用编译器自动生成的函数,就该明确拒绝

1:为驳回编译器自动提供的功能,可以将相应的成员函数声明为private并且不予实现。或者使用Uncopyable这样的base class方法。

class Uncopyable{
    protected:              //允许derived对象构造和析构
        Uncopyable(){}
        ~Uncopyable(){}
    private:                //但是阻止copying
        Uncopyable( const Uncopyable& );
        Uncopyable& operator=(const Uncopyable&);

};
P7:为多态基类声明 virtual 析构函数

1:如果析构函数不是virtual,则当子类对象是经由一个base class指针析构的时候,他的derived部分会没被销毁。

2:多态性质的base class应该声明一个virtual析构函数。如果class带有virtual函数,它就应该有一个virtual析构函数。

3:class设计的目的如果不是作为base class使用,或不是为了具备多态性,就不该声明virtual析构函数。 STL容器vector,list,set,string等都是non-virtual析构。

4:具有多态性的base class的设计目的是为了用来“通过base class接口处理derive对象”。

5:析构函数的运作方式是,最深层派生的那个class其析构函数最先被调用,然后是每个base class的析构函数被调用。

6:抽象类的virtual析构函数应该有一份定义,原因是第5条在子类的析构函数中会调用抽象类的析构函数。

virtual ~AWOW()=0;  //声明抽象析构函数

AWOW::~AWOW()(){}  //抽象析构函数的定义
P8:别让异常逃离析构函数

1:析构函数如果可能抛出异常,应该捕获异常。并且提供一个普通函数给用户机会自己处理该操作。

class DBConn{
    public:
        void close()        //给客户端一个机会处理可能导致异常的行为
        {
            db.close();
            closed = true; 
        }
        ~DBConn()
        {
            if(!closed)
            {
                try{
                    db.close();
                }
                catch(...){
                    //记录下来并结束程序或吞下异常不做处理。
                }
            }
        }
    private:    
        DBConnection db;
        bool closed;
};
P9:绝不在构造和析构过程中调用virtual函数

1:在deviced class对象的base class构造期间,对象的类型还只是base clas对象而不是deviced class对象! virtual函数只会被解析到base class,对象在deviced class的构造函数开始之前不会成为一个deviced class对象。析构函数同样的道理,只是析构顺序自deviced class到base class。

2:可以让deviced class将必要的信息向上传递至base class构造函数去调用non-virtual函数。

P10:令operator= 返回一个 reference to *this

1:令赋值操作符返回一个reference to *this,这样可以实现连锁赋值。如 x=y=z=“str”;

P11:在operator= 中处理自我赋值

1:使用“证同测试”、语句顺序调整、copy-and-swap等方式确保operator=的自我赋值是正确的。

2:确保然后函数如果操作多个对象时,其中有多个对象是同一个对象时,其行为仍然正确。

class Widget{
    void swap(Widget& rhs); //交换*this和rhs的数据,详见P29。
    BitMap* pb;
};

Widget& Widget::operator=(const Widget& rhs)
{
    if(this == &rhs) return *this; //证同测试

    Widget temp(rhs);   //为rhs数据制作一份副本
    swap(temp);         //将*this与副本数据交换;copy-and-swap技术
    return *this;
}
Widget& Widget::operator=(const Widget& rhs)
{
    BitMap* pOrig = pb;
    pb = new BitMap(*rhs.pb);
    delete pOrig;   //这么写的目的是我们需要注意在复制pb前不能删除pb。
    return *this;
}
P12:复制对象时勿忘其每一个成分

1:当编写copying函数时,应确保复制“对象内的所有成员变量”,确保调用所有base class内适当的copying函数复制”所有base class成分”。

2:不要以某个copying函数去实现另一个copying函数,应该把功能放在第三个init函数中去由copying函数调用。

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
    :Customer(rhs)          //调用base class的copy构造函数
{
    logCall("拷贝构造函数");
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
    logCall("复制操作符");
    Customer::operator=(rhs);   //对base class成分进行赋值操作
    priority = rhs.priority;
    retern *this;   
}