如下是一个股票交易的例子:
1 class Transaction // 交易的基类 2 { 3 public: 4 Transaction(); 5 virtual void logTransaction() const = 0; // 用于记录交易日志 6 }; 7 Transaction::Transaction() 8 { 9 logTransaction(); // 调用虚函数 10 } 11 12 class BuyTransaction : public Transaction // 买进股票 13 { 14 virtual void logTransaction() const; 15 }; 16 void BuyTransaction::logTransaction() const{ } 17 18 class SellTransaction : public Transaction // 卖出股票 19 { 20 virtual void logTransaction() const; 21 }; 22 void SellTransaction::logTransaction() const{ } 23 24 int main() 25 { 26 BuyTransaction b; 27 28 return 0; 29 }
上述代码中执行BuyTransaction b时,会调用基类的构造函数,而基类的构造函数会调用一个虚函数来完成工作。但是该虚函数是否会调用派生类的对应函数呢?答案是否定的,解释如下:
1> 基类的构造是先于派生类的构造的。当基类的构造函数执行时,派生类中的成员尚未被初始化,如果允许基类构造期间调用派生类的函数,可能也会调用了没有初始化的“垃圾值”,导致不确定行为发生。
2> 基类构造期间,virtual函数的行为绝对不会伸到派生类中,因为此时构造的是基类,即此时构造的对象的类型是基类而非派生类,这点很重要。
但是有时我们在构造基类的时候又需要派生类中才有的参数信息,如何解决:
在基类中将logTransaction函数改为非虚函数,然后在构造派生类时将必要的参数传递到基类的构造函数中去,像如下这样:
1 #include <string> 2 3 class Transaction // 交易的基类 4 { 5 public: 6 explicit Transaction(const std::string& logInfo); 7 void logTransaction(const std::string& logInfo) const; // 用于记录交易日志 8 }; 9 Transaction::Transaction(const std::string& logInfo) 10 { 11 logTransaction(logInfo); // 调用非虚函数12 } 13 void Transaction::logTransaction(const std::string& logInfo) const{ } 14 15 class BuyTransaction : public Transaction // 买进股票 16 { 17 public: 18 BuyTransaction(const std::string& parameter) : Transaction(parameter){ } 19 }; 20 21 22 int main() 23 { 24 BuyTransaction b("BT"); 25 26 return 0; 27 }
由上可知,如果基类构造时要使用派生类的信息,可以通过派生类构造函数的初始化列表传入相关参数,切不可调妄图调用虚函数实现,因为在构造基类期间其对象的类型是基类类型。
对于析构函数也是同样的道理,基类的析构总是在派生类析构后才进行,此时派生类的对象已经不存在了,而调用虚函数必然会引发一个不确定行为。当然你也可以将基类的虚函数实现后在析构函数中进行调用,但是这样的话,为什么不定义为非虚函数呢,虚函数的意义将不复存在了。
时间: 2024-10-12 14:59:37