C++继承:分文件编写时是否需重声明父类属性?
关于C++类分离后继承与父类属性初始化的问题
嘿,这个问题戳中了很多刚接触C++继承和分离编译的开发者容易踩的坑,咱们来一步步把它讲清楚:
首先明确核心结论:子类继承父类后,完全不需要重新声明父类的属性——这本来就是继承的核心作用之一,子类可以直接访问父类的public/protected成员(只要访问权限允许)。那为什么第二个示例会编译失败呢?咱们对比两个例子分析:
示例一能正常运行的原因
第一个示例是单文件实现,Rectangle作为Shape的public子类,在getArea()里直接访问父类的width和height是完全合法的——因为这两个成员是protected权限,子类有权访问。而且这里并没有在构造函数里初始化父类成员,而是通过setWidth/setHeight方法后续赋值,整个流程符合C++的规则,所以编译运行都没问题。
示例二编译失败的核心原因
第二个示例的问题和“是否分离头文件”其实关系不大,本质是子类构造函数的初始化列表使用错误,具体有两个关键点:
- 父类成员必须由父类构造函数初始化:
你在Programmer的构造函数里写了name(name), surname(surname),但name和surname是父类Person的成员,子类不能在自己的初始化列表里直接初始化父类的成员变量——C++规定,父类的成员只能由父类的构造函数来初始化,子类只能通过调用父类的构造函数来间接完成这个工作。 - 父类缺少带参数的构造函数:
原代码里的Person类只有默认的无参构造函数(编译器自动生成的),没有能接收name和surname的构造函数,所以你就算想调用父类构造函数也没地方调用。
另外还有个小细节:Person是抽象类(有纯虚函数speak()),子类Programmer必须实现这个纯虚函数才能被实例化,原代码里Programmer的speak()实现是对的,但最好在头文件里声明一下void speak() override;,这样更规范,也能让编译器帮你检查是否正确覆盖了父类的虚函数。
修复后的示例代码
头文件(比如命名为person.h)
#include <string> class Person { public: // 给父类添加带参数的构造函数,用于初始化自身的protected成员 Person(std::string n, std::string s) : name(std::move(n)), surname(std::move(s)) {} virtual void speak() = 0; // 纯虚函数,定义抽象类 protected: std::string name; std::string surname; }; class Programmer : public Person { public: // 声明子类构造函数 Programmer(std::string name, std::string surname); // 声明要实现的纯虚函数,加上override确保覆盖正确 void speak() override; };
实现文件(person.cpp)
#include "person.h" #include <iostream> // 子类构造函数:调用父类的带参构造函数初始化父类成员 Programmer::Programmer(std::string name, std::string surname) : Person(std::move(name), std::move(surname)) {} // 实现纯虚函数,这里可以直接访问父类的protected成员name和surname void Programmer::speak(){ std::cout << "My name is " << name << " " << surname << " and I like to code!" << std::endl; // 这里用<<代替+更安全,避免字符串拼接时的类型兼容问题 }
这样修改后,代码就能正常编译运行了。
内容的提问来源于stack exchange,提问作者Luke Collins
相关产品推荐
相关产品推荐

