构造/析构/复制运算

来源:互联网 发布:网络主播经常唱的歌曲 编辑:程序博客网 时间:2024/05/18 00:34
Item5 Know what functions C++ silently writes and calls.

编译器可为一个类默认生成默认构造函数、拷贝构造函数和赋值运算符以及析构函数。注意有指针成员的情况下,默认生成的拷贝构造函数和赋值运算符只是简单的拷贝指针的值,指针指向的数据并不拷贝,即执行的为“浅拷贝”。有可能造成指针指向一个已经释放的内存位置。顾在这样的情况下最好自己实现拷贝构造函数、赋值运算符和析构函数。

Item6 Explicitly disallow the use of compiler-generated functions you do not want.

只要自己定义了Item5照哦给你提到的几个函数,编译器便不再默认生成。如果想阻止对象的拷贝和赋值,可将相应的成员函数声明为private并且不予实现。

如下:

class HomeForSale{public:HomeForSale(){}private:HomeForSale(const HomeForSale&);HomeForSale& operator=(const HomeForSale&);};

或者写一个基类,专门组织拷贝动作

如下:

class Uncopyable{protected:Uncopyable() {}~Uncopyable() {}private:Uncopyable(const Uncopyable&);Uncopyable& operator=(const Uncopyable&);};class HomeForSale : private Uncopyable{};

这样将连接期间的错误转到了编译期间。

Item7 Declare destructors virtual in polumorphic base classes.

如果一个基类为实现多态而设计,则应该为其声明一个virtual析构函数。如果class带有任何virtual函数,也应该拥有一个virtual析构函数。

否则出现的问题如下:如果用一个基类指针指向派生类的对象,则在delete基类指针的时候,会出现派生类成分没有被销毁的后果。

如果一个类的设计不是作为基类使用,不是为了多态性,不应该声明virtual析构函数。

Item8 Prevent exceptions from leaving destructors.

析构函数不要抛出异常。如果被析构函数调用的函数可能抛出异常,则析构函数应该捕捉异常,然后吞下他们。

以下为close抛出异常后,程序异常终止。或者去掉std::abort的调用,析构函数将异常吞下。

class DBConnection{public:static DBConnection create();void close();   //失败则抛出异常};class DBConn{public:~DBConn(){try{db.close();}catch(...){
                        制作运转记录,记下对close的调用失败;
std::abort();}}private:DBConnection db;};


如果客户需要对某个操作函数运行期间抛出的异常做出反应,则类应该提供一个普通的函数执行该该做,而非析构函数。

 

class DBConn{public:void close(){db.close();closed=true;}~DBConn(){try{if(!closed){db.close();}}catch(...){std::abort();}}private:DBConnection db;bool closed;};

由客户在调用DBConn的close的过程中,去捕捉异常并进行一场处理。

Item9 Never class virtual functions during constructions or destructions.

在构造函数和析构函数中,调用virtual函数,类调用从不下降至派生类。因为在构造的过程中,先构造基类的成员,此时基类还未构造,顾不可能调用一个未构造的对象的成员。“早基类构造期间,virtual函数不是virtual函数”。析构也是同样的道理,析构的过程与构造的过程相反,等到调用基类的析构函数的时候,派生类的析构函数已经调用过。

Item10 Have assignment operators return a reference to *this.

class Widget{public:Widget& operator=(const Widget& rhs){....return *this;}};


对于+=、-=、*=等也成立。

Item11 Handle assignment to self in operator=.

处理方式有如下几种:(1)进行“自我赋值”的检查:

class Widget{public:Widget& operator=(const Widget& rhs){if(this==&rhs)return *this;...}};


(2)复制原有对象的内容

class Bitmap{};class Widget{public:Widget& operator=(const Widget& rhs){Bitmap* pOrig=pb;pb=new Bitmap(*rhs.pb);delete pOrig;return *this;}private:Bitmap *pb;};

(3)copy and swap技术

class Widget{public:void swap(Widget& rhs);Widget& operator=(const Widget& rhs);};Widget& Widget::operator=(const Widget& rhs){Widget temp(rhs);swap(temp);return *this;}

Item 12 Copy all parts of an object.

如果要自定义拷贝构造函数和赋值运算符,则要注意拷贝对象内的所有成员变量及基类的成份。

例如:

class Customer{public:Customer(const Customer& rhs){this->name=rhs.name;}Customer& operator=(const Customer& rhs){if(this==&rhs) return *this;this->name=rhs.name;return *this;}private:std::string name;};class PriorityCustomer:public Customer{public:PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs),priority(rhs.priority){}PriorityCustomer& operator=(const PriorityCustomer& rhs){Customer::operator=(rhs);priority=rhs.priority;return *this;}private:int priority;};


另外,不要尝试在某个拷贝函数实现另一个拷贝函数。应该将重复代码部分放进第三个函数,供拷贝构造函数和赋值运算符调用。

原创粉丝点击