为何调用基类operator()而非派生类?如何调用派生类operator()?
为什么我的表达式类调用的是基类的
operator()而非派生类? 嘿,这个问题的核心原因是对象切片(Object Slicing)——C++里新手很容易踩的一个经典坑!
问题根源
你看你定义的e_add结构体里,_left和_right是expr类型的直接对象,而不是指针或者引用。当你把const_value对象传给e_add的构造函数时,编译器会只拷贝const_value里属于基类expr的部分,派生类特有的成员(比如const_value的_value)会被直接"切掉"。这时候调用_left(arg),本质上是在调用基类expr的operator(),完全触发不了派生类的虚函数覆盖逻辑。
简单来说:C++的虚函数多态必须通过指针或引用来实现,直接存储基类对象会丢失派生类的类型信息,导致多态失效。
解决方法:用指针/智能指针保存基类引用
我们可以把e_add里的成员改成智能指针(推荐,更安全,自动管理内存),或者原始指针。下面是用std::unique_ptr修正后的完整代码:
#include <iostream> #include <cmath> #include <memory> // 引入智能指针头文件 using namespace std; struct expr { virtual double operator()(double) { cerr << "EXPR::operator() called\n"; return 100500; }; // 基类必须加虚析构函数,确保派生类析构函数能被正确调用 virtual ~expr() = default; }; struct const_value : public expr { double _value; const_value(double value = 0): _value(value) {}; double operator()(double arg = 0) override { cout << "const_value " << _value << " called arg = " << arg << '\n'; return _value; }; }; #define make_bin_op(_op_name, _op) \ struct e_##_op_name : public expr { \ unique_ptr<expr> _left; \ unique_ptr<expr> _right; \ // 用move转移智能指针所有权,避免拷贝 e_##_op_name(unique_ptr<expr> left, unique_ptr<expr> right): _left(move(left)), _right(move(right)) {}; \ double operator()(double arg) override { \ return (*_left)(arg) _op (*_right)(arg); \ }; \ } make_bin_op(add, +); #undef make_bin_op int main() { auto c1 = make_unique<const_value>(1); e_add e{move(c1), make_unique<const_value>(1)}; cout << e(100) << endl; auto c2 = make_unique<const_value>(1); cout << (*c2)(100) << endl; return 0; }
关键修正点
- 将
e_add的_left/_right改为unique_ptr<expr>,通过智能指针保留派生类的类型信息,实现多态调用。 - 基类添加了虚析构函数,防止内存泄漏(如果用原始指针的话,也需要手动管理析构)。
- 用
make_unique创建智能指针,构造e_add时用move转移所有权,避免不必要的拷贝。
如果不想用智能指针,也可以用原始指针,但要注意手动管理内存(比如在析构函数里释放指针),不过智能指针的安全性更高,更推荐使用。
内容的提问来源于stack exchange,提问作者Nex
相关产品推荐
相关产品推荐

