为什么可以从基类访问派生类的私人虚拟成员功能
考虑以下代码片段:
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base::constr" << std::endl;
print();
}
virtual ~Base() = default;
void print() const { printImpl(); }
private:
virtual void printImpl() const {
std::cout << "Base::printImpl" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived::constr" << std::endl;
}
private:
void printImpl() const override {
std::cout << "Derived::printImpl" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
ptr->print();
delete ptr;
}
以上代码将打印以下内容:
Base::constr
Base::printImpl
Derived::constr
Derived::printImpl
但是我不明白printimpl
private private 函数可以从基本的print 功能。在我的理解中,
函数)的同一类,在这里,此
指针隐式传递给print
函数保留了派生的对象的地址,但是我认为私有成员函数只能从成员函数(以及friend
base
类与derived
的类不同,尽管有一个是
代码>关系。
Consider the following snippet of code:
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base::constr" << std::endl;
print();
}
virtual ~Base() = default;
void print() const { printImpl(); }
private:
virtual void printImpl() const {
std::cout << "Base::printImpl" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived::constr" << std::endl;
}
private:
void printImpl() const override {
std::cout << "Derived::printImpl" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
ptr->print();
delete ptr;
}
The above code will print the following:
Base::constr
Base::printImpl
Derived::constr
Derived::printImpl
but I don't understand why printImpl
private function is accessible from the base's print
function. In my understanding this
pointer implicitly passed to the print
function holds the derived object's address, but I thought private member functions could be called ONLY from the member functions (and from friend
functions) of the same class and here, Base
class is not the same class as Derived
, although there is an is a
relationship.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
首先,正如 @Eljay 所指出的 - printImpl() 是
Base
类的一个方法,尽管是虚拟的。因此,可以从基类访问它。Derived
只是提供了它的不同实现。虚函数的全部意义在于,您可以使用基类引用或指针来调用子类的重写。换句话说,
private
只考虑子类的访问;从类的基类中保留一些私有
是没有意义的:如果一个方法对于基类来说是已知的,那么它一定是基类的方法。 . 虚拟方法。话虽如此 - 请注意
printImpl()
的Derived
版本实际上无法从print()
访问 -当它在基类构造函数中调用时。这是因为在调用过程中,构造的 vtable 只是Base
的 vtable,因此printImpl
指向Base::printImpl
。事实上,
print()
是Base
的成员,它调用printImpl( )
-Base
的另一种方法。First, as @Eljay notes -
printImpl()
is a method, albeit virtual, of theBase
class. So, it's accessible from the base class.Derived
merely provides a different implementation of it. And the whole point of virtual functions is that you can call a subclass' override using a base class reference or pointer.In other words,
private
only regards access by subclasses; it's meaningless to keep somethingprivate
from a class' base class: If a method is at all known to the base class, it must be a method of the base class... a virtual method.Having said all that - note that the
Derived
version ofprintImpl()
is effectively inaccessible fromprint()
- when it's invoked within the base class constructor. This is because during that call, the constructed vtable is only that ofBase
, soprintImpl
points toBase::printImpl
.And indeed,
print()
is a member ofBase
, which invokesprintImpl()
- another method ofBase
.基类的私有功能在派生类中无法访问,但可以过度延长,因为这两个是独立的概念。
对于
打印
从base
class的构造函数,有以下规则:PS通常可以避免使用构造函数或破坏者的虚拟函数的通话
The private function of base class is not accessible in the derived class, but it can be overriden, since these two are separate concepts.
For the
print
call from the constructor of theBase
class, there is a following rule:P.S. In general calls to virtual functions from constructors or destructors better be avoided
情况 1
这里我们考虑以下语句:
这些是上述语句的效果:
步骤 1) 使用 默认值在堆上创建
Derived
类型的对象构造函数Derived::Derived()
。第 2 步) 在进入默认构造函数
Derived::Derived()
的主体之前,编译器隐式调用默认构造函数Base::Base()
代码>.这样我们就得到了输出:Step 3) 接下来,遇到
Base::Base()
中的print()
语句。该语句相当于这样写:因此,
Base
类的print()
函数被调用。第4步) 在
Base
的print
成员函数中,遇到调用语句printImpl();
。这个语句相当于写:但注意,目前,
Base
类型的子对象的构造正在进行中。这意味着整个对象的Derived
部分尚未构造。因此,即使printImpl
是一个虚拟成员函数并且调用是通过指针进行的,但此时虚拟机制已被禁用。因此,this->printImpl()
调用Base::printImpl
版本,而不是printImpl
的派生版本。发生这种情况是因为派生类尚未初始化。这样我们就得到了输出:第 5 步)最后,默认构造函数
Derived::Derived()
的主体被执行。因此我们得到输出:第 6 步)指针
ptr
指向新创建的派生对象的Base
部分。案例 2
这里我们考虑以下陈述:
现在,来自此 pionter 的文档:
所以,当你写:
上面的语句相当于写:
这意味着指针
ptr
指向的对象的地址(只不过是ptr
本身)隐式传递到print
成员函数的隐式this
参数。现在,在
print
成员函数内部,包含对printImpl()
的调用的语句相当于:根据 在我的回答开头引用了声明。从同一个引用语句中,由于成员
printImpl
是一个虚拟成员函数,因此表达式this->printImpl()
结果是虚拟函数调用,这意味着它会导致调用派生类printImpl
函数。因此我们得到输出:Case 1
Here we consider the statement:
These are the effects of the above statement:
Step 1) An object of type
Derived
is created on the heap using the default ctorDerived::Derived()
.Step 2) Before entering the body of the default ctor
Derived::Derived()
, the compiler imlicitly calls the default ctorBase::Base()
. Thus we get the output:Step 3) Next, the statement
print()
insideBase::Base()
is encountered. This statement is equivalent to writing:Hence, the
print()
function of classBase
is called.Step 4) Inside the
print
member function ofBase
, the call statementprintImpl();
is encountered. This statement is equivalent to writing:But note that currently, the construction of the subobject of type
Base
is going on. Meaning theDerived
part of the whole object has not yet been constructed. So even though theprintImpl
is a virtual member function and the call is made through a pointer, the virtual mechanism is disabled at this point. And thus thethis->printImpl()
calls theBase::printImpl
version instead of the derived version ofprintImpl
. This happened because the derived class has not yet been initialized. Thus we get the output:Step 5) Finally, the body of the default ctor
Derived::Derived()
is executed. And hence we get the output:Step 6) The pointer
ptr
points to theBase
part of the newly created derived object.Case 2
Here we consider the statement:
Now, from this pionter's documentation:
So, when you wrote:
The above statement is equivalent to writing:
This means that the address of the object to which the pointer
ptr
points(which is nothing butptr
itself) is implicitly passed to the implicitthis
parameter of theprint
member function.Now, inside the
print
member function, the statement containing the call toprintImpl()
is equivalent to:according to the quoted statement at the beginning of my answer. And from the same quoted statement, since the member
printImpl
is a virtual member function, the expressionthis->printImpl()
results in a virtual function call, meaning it results in calling the derived classprintImpl
function. And hence we get the output:在代码中打印
Base::printImpl
的原因是它是从Base
的构造函数调用的。派生类尚未构造,因此所有对虚函数的调用都将引用Base
的版本。构造后,对 print 的调用通常会重定向到派生类的版本。函数是否被标记为
私有、公共、受保护
在这里并不重要。The reason that in the code you have
Base::printImpl
printed is because it is called from constructor ofBase
. The derived class wasn't constructed yet, so all calls to virtual functions will be referred toBase
's versions.After construction the calls to
print
will akways redirect towards derived class's version. Whether functions are markedprivate, public, protected
is inconsequential here.