Effective C++改善程序与设计的55个具体做法2 构造/析构/赋值运算

条款05: Kown what functions C++ silently writes and calls.

class Empty {
}; 
等价于如下的类
class Empty{
publicEmpty(){};Empty(const Empty& rhs){}~Empty(){}   Empty operation = (const Empty& rhs) {}
};
下面代码造成上述每一个函数被编译器产出:
Empty e1;     // default构造函数// 析构函数
Empty e2(e1); // copy构造函数 
e2 = e1;      // copy assignment操作符 

区分一下拷贝构造与拷贝赋值:

#include 
using namespace std;class CompareCopyAssignAndCopyCtr{
public:CompareCopyAssignAndCopyCtr(){cout << "CompareCopyAssignAndCopyCtr"<< endl;};	~CompareCopyAssignAndCopyCtr(){cout << "~CompareCopyAssignAndCopyCtr"<< endl;};	CompareCopyAssignAndCopyCtr(const CompareCopyAssignAndCopyCtr&){cout << "Copyctr"<< endl;};CompareCopyAssignAndCopyCtr& operator=(const CompareCopyAssignAndCopyCtr&){cout << "Copyassign"<< endl;return *this;};
}; int main(int argc, char** argv) {CompareCopyAssignAndCopyCtr c1;CompareCopyAssignAndCopyCtr c2;cout << "---------------------"<< endl;CompareCopyAssignAndCopyCtr c3(c2);   //copy ctr cout << "---------------------"<< endl;c2 = c1;                             //copy assigncout << "---------------------"<< endl;CompareCopyAssignAndCopyCtr c4 = c1; //copy ctr cout << "---------------------"<< endl;return 0;
}
运行结果如下:
#include 
using namespace std;template 
class NamedObject{
public:NamedObject(const string& name, const T& value);void printInfo();
private:string nameValue;T objectValue;
};template 
NamedObject::NamedObject(const string& name, const T& value){nameValue = name;objectValue = value;
};template
void NamedObject::printInfo(){cout << "NamedObject's Name is " << nameValue << endl;
};int main(int argc, char** argv) {string newDog("Lucky");	string oldDog("Hasq");NamedObject p(newDog, 2);NamedObject s(oldDog, 36);p.printInfo();s.printInfo();p = s;p.printInfo();s.printInfo(); return 0;
}
运行结果如下: 如果调用默认的构造函数
int main(int argc, char** argv) {NamedObject m;return 0;
}
编译报错: [Error] no matching function for call to 'NamedObject::NamedObject()'
由于其中声明了一个构造函数,编译器于是不再为它创建default构造函数。为了说明这一点,再举一个例子
using namespace std;class Fruit {
public:Fruit(int id);string s;
};Fruit::Fruit(int id){
};class Apple: public Fruit {
public:Apple();
};Apple::Apple(){
};int main () {Apple apple;
}

编译报错: [Error] no matching function for call to 'Fruit::Fruit()'

#include 
using namespace std;
template 
class NamedObject{
public:NamedObject(string& name, const T& value);void printInfo();
private:string& nameValue;const T objectValue;
};template 
NamedObject::NamedObject(string& name, const T& value): nameValue(name), objectValue(value) {
}template
void NamedObject::printInfo(){cout << "NamedObject's Name is " << nameValue << endl;
};int main(int argc, char** argv) {string newDog("Lucky");string oldDog("Hasq");NamedObject p(newDog, 2);NamedObject s(oldDog, 36);p.printInfo();s.printInfo();p = s;p.printInfo();s.printInfo(); return 0;
}

编译报错:            [Error] non-static reference member 'std::string& NamedObject::nameValue', can't use default assignment operator [Error] non-static const member 'const int NamedObject::objectValue', can't use default assignment operator 结论: ①如果打算在一个“内含reference 成员”的class类支持赋值操作,必须自己定义copy assignment 操作符 ②面对“内含const成员”的class类,编译器的反应也是一样。

条款06: Explicitly disallow the use of complier-generated functions you do not want.

class HomeForSale{
};int main(int argc, char** argv) {HomeForSale h1;HomeForSale h2;HomeForSale h3(h1); //企图拷贝h1 --期望不该通过编译  h1 = h2;            //企图拷贝h2 --期望不该通过编译 return 0;
}
为实现阻止拷贝的作用,可将相应的成员函数声明为private并且不予实现。

class Uncopyable{
protected:Uncopyable(); //允许derived对象构造和析构 ~Uncopyable(); 
private:Uncopyable(const Uncopyable&);Uncopyable& operator=(const Uncopyable&);
}; Uncopyable::Uncopyable(){
};Uncopyable::~Uncopyable(){
};class Child: public Uncopyable{
}; int main(int argc, char** argv) {Child child1;Child child2;Child child3(child2);child2 = child1;return 0;
}

此时编译报错:

[Error] 'Uncopyable::Uncopyable(const Uncopyable&)' is private

[Error] 'Uncopyable& Uncopyable::operator=(const Uncopyable&)' is private

条款07: Declare destructors virtual in polymorphic base classes

#include 
using namespace std;class TimeKeeper{
public:TimeKeeper(){cout << "TimeKeeper" << endl;	};~TimeKeeper(){cout << "~TimeKeeper" << endl;	};		
};class AtomicClock: public TimeKeeper{
public:AtomicClock(){cout << "AtomicClock" << endl;	};~AtomicClock(){cout << "~AtomicClock" << endl;	};		
};	 class WaterClock: public TimeKeeper{
}; class WristWatch: public TimeKeeper{
}; TimeKeeper* getTimeKeeper(int type){TimeKeeper* temp = NULL;switch(type){case 1:temp = new AtomicClock();break;case 2:temp = new WaterClock();break;case 3:temp = new  WristWatch();break;default:break;}return temp;
}; int main(int argc, char** argv) {TimeKeeper* ptk = getTimeKeeper(1);delete ptk;return 0;
}

运行结果如下:


补充:构造和析构函数链

int main(int argc, char** argv) {AtomicClock ac;return 0;
}
运行结果如下:


结论:

derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,实际执行时通常发生的是对象的derived成分没有被销毁。

改进做法,将析构函数声明为virtual函数即可,如下所示。

class TimeKeeper{
public:...virtual ~TimeKeeper(){cout << "~TimeKeeper" << endl;	};		
};
运行结果如下:


注意:

①带有多态(polymorphic)性质的base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数(???)。

②classes的设计目的如果不是作为base classes使用,或不是为了具备多态性(polymorphically),就不该声明virtual析构函数

条款 08:Prevent exceptions from leaving destructos

#include 
using namespace std;class DBConnection{
public:static DBConnection create(){DBConnection dbc;cout << "Connection Success!" << endl; return dbc;  };void close(){cout << "Connection Close!" << endl; };
}; class DBConn{
public:DBConn(){
    db = DBConnection::create();	} void close(){                 //供客户使用的新函数 db.close();closed = true;};~DBConn(){if (!closed) {try {db.close();            //关闭连接(如果客户没有显示调用close()函数的话) }catch(...){              //如果关闭动作失败,记录下来并结束程序或吞下异常 //制作运转记录,记下对close的调用失败   }}};
private:DBConnection db;bool closed;
}; int main(int argc, char** argv) {DBConn dbconn;return 0;
}
运行结果如下: 注意: ①析构函数绝对不要吐出异常。 ②如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部