《Effective C++》条款9:不在构造和析构函数中调用virtual函数

本章主要讲解了多态情况下在构造函数内调用virtual的问题;

 

针对于多态情况下,我们希望根据指针指向的类型调用不同的虚函数,但是在构造函数中可能存在问题;

例如下列例子:

class Transaction {
public:
	Transaction();
	virtual void logTransaction() const = 0;//pure virtual函数;
};

Transaction::Transaction() {
	//...
	logTransaction();
}

class BuyTransaction :public Transaction {
public:
	virtual void logTransaction() const;
};

按照多态性定义,如果我们对子类BuyTransaction进行构建:

BuyTransaction bt;

根据构造函数调用链,首先先调用Transaction函数,由于构造函数内调用二零虚函数logTransaction(),按照多态定义,应该调用bt的logTransaction(),但是实际并不如此;

实际调用的还是基类的logTransation()函数;

 

主要原因如下:

1.在实际构造和析构阶段,所有virtual函数都是视为非虚函数;

2.在基类部分构造和析构阶段,派生类仍然是视之为基类,并不当作派生类来使用;

 

所以,如果在构造和析构中使用虚函数并不能达到多态行为,所以应该避免这种行为;

 

值得注意的是:通过函数嵌套调用间接调用虚函数仍然存在这种预期之外的错误,唯一的区别是编译器无法显式的检查出来;

class Transaction {
public:
	Transaction();
	virtual void logTransaction() const = 0;//pure virtual函数;
private:
	void init() {
		logTransaction();
	}
};

Transaction::Transaction() {
	//...
	init();
}

如上所示,编译器无法检查出析构和构造函数中虚函数的嵌套调用,实际调用结果和之前的一样;

所以总而言之:构造和析构期间不要调用virtual函数,因为此类调用从不下降到derived class层面;

class Transaction {
public:
	explicit Transaction(const std::string& logInfo);
	void logTransaction(const std::string& logInfo) const;//pure virtual函数;
private:
};

Transaction::Transaction(const std::string& logInfo) {
	logTransaction(logInfo);
}

class BuyTransaction :public Transaction {
public:
	BuyTransaction(const std::string& parameters):Transaction(createLogString(parameters)) {
		//do something;
	}
private:
	static std::string createLogString(const std::string& parameters);
};

该方法具有一定的技巧性:

首先将虚函数变成基类普通函数,根据输入参数值进行构造;

派生类根据自己的类型传入不同的parameters来通过createLogString构建出不同的参数值,传入基类构造函数,基类构造函数调用log函数,来达到不同的类具有不同的信息输出,从而达到多态的同等作用;

 

骚操作,需要细细体会一下;

 

posted @ 2020-12-03 23:31  暮云林凌  阅读(108)  评论(0)    收藏  举报