C++继承中派生类调用add()无法正确修改成员变量问题
问题根源
这个问题和赋值运算符重载没有任何关系,核心是对C++继承下的成员作用域、名字查找逻辑理解有偏差:
- 你在派生类
Derived里重新声明了和基类Base同名的私有成员m_value,这两个变量是完全独立的实体,分别存在于派生类对象的基类区段、派生类自有区段,不存在派生类成员覆盖基类成员的效果。 getValue()是基类定义的非虚函数,它的名字查找和作用域永远绑定在基类,只会访问基类自身的m_value,完全感知不到派生类里声明的同名变量。
实际代码执行流程是:
- 构造
Derived对象时,基类构造函数先把基类区段的m_value初始化为5,之后派生类构造函数把派生类自有的m_value也初始化为5 - 调用
derived.add(2)时,匹配到派生类重写的add方法,修改的是派生类自有的m_value,把它从5改成了7,但基类区段的m_value从头到尾没有被修改,还是初始值5 - 调用
derived.getValue()时,因为派生类没有重写这个函数,实际执行的是基类版本,返回的是从未被改动过的基类m_value,所以两次打印结果都是5。
你之前加的派生类赋值运算符重载完全是无效修改,因为测试代码里根本没有对Derived对象做赋值操作,这段代码从来不会被执行到。
修复方案
根据实际设计目标,选以下两种方案之一即可:
方案1:派生类不需要独立的m_value,直接复用基类成员
这是绝大多数场景下的推荐写法,不需要在派生类里重复定义同名成员,把基类的m_value访问权限设为protected开放给派生类访问即可:
#include <iostream> class Base { protected: // 派生类可以直接访问,外部无法访问 int m_value {}; public: Base(int value = 5): m_value {value} {} virtual int getValue() { return m_value; } virtual void add(int addend) { m_value = m_value + addend; } }; class Derived: public Base { public: // 通过基类构造函数初始化继承来的m_value Derived(int value = 5): Base(value) {} // 用override显式标记虚函数重写,编译器会帮你检查签名正确性 void add(int addend) override { m_value = m_value + addend; } }; int main() { Derived derived {}; std::cout << derived.getValue() << "\n"; // 输出5 derived.add(2); std::cout << derived.getValue() << "\n"; // 输出7,符合预期 return 0; }
方案2:派生类确实需要独立的m_value,重写所有相关基类方法
如果有明确设计要求派生类必须持有独立于基类的m_value,就必须把所有访问该成员的基类方法全部重写,确保调用时走到派生类版本:
#include <iostream> class Base { private: int m_value {}; public: Base(int value = 5): m_value {value} {} // 必须设为虚函数才能触发多态调用 virtual int getValue() { return m_value; } virtual void add(int addend) { m_value = m_value + addend; } }; class Derived: public Base { private: int m_value {}; // 派生类自有的独立成员 public: Derived(int value = 5): Base(), m_value {value} {} // 重写getValue返回派生类自己的m_value int getValue() override { return m_value; } void add(int addend) override { m_value = m_value + addend; } }; int main() { Derived derived {}; std::cout << derived.getValue() << "\n"; // 输出5 derived.add(2); std::cout << derived.getValue() << "\n"; // 输出7 return 0; }
注意:日常开发中除非有非常明确的设计需求,否则强烈不推荐在派生类定义和基类同名的成员,这种写法会触发名字隐藏,极易出现你遇到的这类难以排查的逻辑bug。重写虚函数时建议始终加上
override关键字,编译器会在你写错函数签名、没有成功重写时直接报错,避免低级错误。
内容的提问来源于stack exchange,提问作者epicninja_gamertriceratops
相关产品推荐
相关产品推荐

