C++继承中嵌套类访问protected成员权限问题及解决方案咨询
问题场景
在实现均匀迷宫生成算法时,遇到了继承体系中嵌套类的protected成员访问权限问题:
基类lab_bui提供基础功能:
class lab_bui { protected: void say_hi() { std::cout << "hi!" << std::endl; } };
中间类uni_lab_bui继承自lab_bui,其嵌套类Builder可以正常访问lab_bui的protected成员:
class uni_lab_bui : public lab_bui { protected: class Builder { public: Builder(uni_lab_bui* obj) : enclosing{obj} {} void say_bye() { std::cout << "bye!" << std::endl; this->enclosing->say_hi(); } // 无错误,嵌套类可访问外部类的protected成员 protected: uni_lab_bui* enclosing; }; };
但子类aldous_broder的嵌套类AB_Builder继承自uni_lab_bui::Builder后,无法通过enclosing指针访问lab_bui的say_hi方法:
class aldous_broder : public uni_lab_bui { public: void ult() { AB_Builder b(this); b.speak(); } protected: size_t field{ 0ull }; class AB_Builder : public uni_lab_bui::Builder { public: AB_Builder(aldous_broder* obj) : Builder(obj){} void speak() { this->enclosing->say_hi(); // 编译报错:无法访问protected成员 this->say_bye(); // 此调用正常,因为say_bye是父类Builder的public方法 } }; };
问题本质
enclosing是uni_lab_bui*类型,根据C++访问规则,只有uni_lab_bui及其直接友元能通过该类型指针访问lab_bui的protected成员。aldous_broder::AB_Builder属于aldous_broder的嵌套类,不在uni_lab_bui的访问权限范围内,因此直接调用say_hi会触发权限错误。
不破坏封装的解决方案
方案1:在uni_lab_bui中提供protected转发接口
在uni_lab_bui中新增一个protected方法,专门用于访问lab_bui的say_hi,让子类嵌套类可以通过该接口间接访问:
class uni_lab_bui : public lab_bui { protected: // 新增转发接口 void call_say_hi() { say_hi(); } class Builder { public: Builder(uni_lab_bui* obj) : enclosing{obj} {} void say_bye() { std::cout << "bye!" << std::endl; this->enclosing->say_hi(); } protected: uni_lab_bui* enclosing; }; };
修改AB_Builder的调用方式:
void speak() { this->enclosing->call_say_hi(); // 通过转发接口访问 this->say_bye(); }
该方案保持封装性,lab_bui的say_hi依然只对继承体系内开放,通过uni_lab_bui的接口统一控制访问。
方案2:将Builder改为模板类适配子类指针
把uni_lab_bui::Builder改成模板类,让其enclosing类型匹配具体子类,这样AB_Builder中的enclosing就是aldous_broder*类型,而aldous_broder作为lab_bui的直接子类,有权限访问say_hi:
class uni_lab_bui : public lab_bui { protected: template<typename Derived> class Builder { public: Builder(Derived* obj) : enclosing{obj} {} void say_bye() { std::cout << "bye!" << std::endl; this->enclosing->say_hi(); } protected: Derived* enclosing; }; };
修改AB_Builder的继承方式:
class AB_Builder : public uni_lab_bui::Builder<aldous_broder> { public: AB_Builder(aldous_broder* obj) : Builder(obj){} void speak() { this->enclosing->say_hi(); // enclosing为aldous_broder*,可直接访问 this->say_bye(); } };
该方案通过模板让嵌套类适配不同子类,既保留灵活性,又符合C++访问权限规则。
方案3:复用父类Builder的方法间接访问
在uni_lab_bui::Builder中新增专门访问say_hi的方法,AB_Builder直接调用该方法即可:
class uni_lab_bui : public lab_bui { protected: class Builder { public: Builder(uni_lab_bui* obj) : enclosing{obj} {} void say_bye() { std::cout << "bye!" << std::endl; this->enclosing->say_hi(); } // 新增访问say_hi的方法 void call_hi() { this->enclosing->say_hi(); } protected: uni_lab_bui* enclosing; }; };
修改AB_Builder的调用逻辑:
void speak() { this->call_hi(); // 调用父类Builder的方法间接访问 this->say_bye(); }
该方案最简洁,完全依托现有嵌套类的权限,不需要修改类型体系,仅扩展父类嵌套类的接口即可。
内容的提问来源于stack exchange,提问作者Rostislav Frolov

