C++中派生类未定义拷贝构造器为何能复制基类私有成员?
在类B的自定义拷贝构造器(参数为string lan、const B& kop)的初始化列表中,我调用了未定义的B(kop),编译器自动生成该拷贝构造器后,居然能复制基类A的私有成员lan、licz以及B自身的成员tf。我原本认为基类私有成员必须通过公共setter才能访问,而且licz根本没有setter,但程序却正常运行,想从技术角度搞清楚编译器的处理逻辑。
相关代码
abc.h
#ifndef ABC_H_INCLUDED #define ABC_H_INCLUDED #include <iostream> #include <string> #include <iomanip> using namespace std; class A { private: string lan; int licz; public: A(string lan, int licz) : lan(lan), licz(licz) {} virtual void drukuj() const { cout<<lan<<" ; "<<licz<<endl; } string getLen() const { return lan; } int getLicz() const { return licz; } void setString(string lan) { this->lan=lan; } }; class B : public A { private: bool tf; public: B(string lan, int licz, bool tf) : A(lan,licz), tf(tf){} B(string lan, const B& kop) : B(kop) { A::setString(lan); } void drukuj() const override { cout<<boolalpha<<A::getLen()<<" ; "<<A::getLicz()<<" ; "<<tf<<endl; } }; #endif // ABC_H_INCLUDED
main.cpp
#include "abc.h" int main() { A a("obiekt klasy A",1); B b("obiekt klasy B", 2, true); B c("kopia obiektu b", b); a.drukuj();//drukuje wszystkie składowe obiektu a b.drukuj(); c.drukuj(); return 0; }
1. 编译器自动生成拷贝构造函数的核心逻辑
你在B的构造器初始化列表中写B(kop)是委托构造语法,委托给B的拷贝构造函数。由于你没有显式定义B的拷贝构造函数,编译器会自动生成一个默认拷贝构造函数,它会完成两个关键操作:
- 调用基类
A的默认拷贝构造函数(A也无显式定义,编译器同样会自动生成); - 逐成员拷贝
B自身的非静态成员(即tf)。
2. 基类私有成员的复制规则
你误以为基类私有成员只能通过setter访问,这是类外部代码的访问限制。但编译器生成的拷贝构造函数属于类的特殊成员函数,天生拥有对类所有成员(包括私有成员)的访问权限。
对于A的自动生成拷贝构造函数来说,它会直接读取源对象的私有成员lan和licz,将值逐位(或逐成员,如string这类非POD类型)复制到新对象的对应成员中,完全不需要通过getter/setter。而B的自动生成拷贝构造函数会自动调用A的拷贝构造函数,完成基类部分的复制,这是编译器的内置行为,无需手动干预。
3. 自定义构造器的执行流程
你的B(string lan, const B& kop)构造器执行顺序是:
- 先委托调用
B的自动生成拷贝构造函数,完成基类A的lan、licz和B自身tf的复制; - 然后在构造函数体中调用
A::setString(lan),将基类的lan成员替换为你传入的新字符串。
这就是最终c对象的lan是"kopia obiektu b",而licz、tf与b一致的原因——licz通过A的拷贝构造函数复制后未被修改,tf则由B的拷贝构造函数直接复制。
4. 为何不需要显式setter
编译器生成的特殊成员函数(拷贝构造、拷贝赋值等)是C++语言底层提供的对象复制机制,它们属于类的内部实现逻辑,不受成员访问权限的约束。getter/setter是给类外部代码提供的访问接口,底层的对象复制操作完全不需要经过这一层。
内容的提问来源于stack exchange,提问作者urshuk

