gpt4 book ai didi

c++ - 为什么覆盖虚函数时不考虑访问限定符?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:50:42 24 4
gpt4 key购买 nike

以下代码打印“I'm B!”。这有点奇怪,因为 B::foo() 是私有(private)的。关于A* ptr,我们可以说它的静态类型是A(foo是public),它的动态类型是B(foo 是私有(private)的)。所以我可以通过指向 A 的指针调用 foo。但是这样我就可以访问 B 中的私有(private)函数。可以算作封装违规吗?

由于访问限定符不是类方法签名的一部分,因此会导致这种奇怪的情况。为什么在覆盖虚函数时不考虑 C++ 中的访问限定符?我可以禁止这种情况吗?这个决定背后的设计原则是什么?

Live example .

#include <iostream>

class A
{
public:
virtual void foo()
{
std::cout << "I'm A!\n";
};
};

class B: public A
{
private:
void foo() override
{
std::cout << "I'm B!\n";
};
};

int main()
{
A* ptr;
B b;

ptr = &b;

ptr->foo();
}

最佳答案

你有很多问题,所以我会尽量一一回答。

为什么重写虚函数时不考虑 C++ 中的访问限定符?

因为在所有重载决议之后,编译器都会考虑访问限定符。这种行为由标准规定。

例如,参见 on cppreference :

Member access does not affect visibility: names of private and privately-inherited members are visible and considered by overload resolution, implicit conversions to inaccessible base classes are still considered, etc. Member access check is the last step after any given language construct is interpreted. The intent of this rule is that replacing any private with public never alters the behavior of the program.

下一段描述了您的示例所展示的行为:

Access rules for the names of virtual functions are checked at the call point using the type of the expression used to denote the object for which the member function is called. The access of the final overrider is ignored.

另请参阅 this answer 中列出的操作顺序.

我可以禁止这种情况吗?

没有。

而且我认为您永远无法这样做,因为这种行为并不违法。

这个决定背后的设计原则是什么?

澄清一下:这里的“决定”是指编译器在重载解析后检查访问限定符的规定。简短的回答:防止在更改代码时出现意外。

有关更多详细信息,我们假设您正在开发一些如下所示的 CoolClass

class CoolClass {
public:
void doCoolStuff(int coolId); // your class interface
private:
void doCoolStuff(double coolValue); // auxiliary method used by the public one
};

假设编译器可以根据公共(public)/私有(private)说明符进行重载解析。然后下面的代码将成功编译:

CoolClass cc;
cc.doCoolStuff(3.14); // invokes CoolClass::doCoolStuff(int)
// yes, this would raise the warning, but it can be ignored or suppressed

然后在某些时候您发现您的私有(private)成员函数实际上对类客户端有用并将其移至“公共(public)”区域。这会自动更改预先存在的客户端代码的行为,因为现在它调用CoolClass::doCoolStuff(double)

因此,应用访问限定符的规则是以不允许允许这种情况的方式编写的,因此您会在一开始就遇到“模棱两可的调用”编译器错误。出于同样的原因,虚函数也不是特例(参见 this answer)。

可以算作封装违规吗?

不是真的。通过将指向您的类的指针转换为指向其基类的指针,您实际上是在说:“在此我想使用这个对象 B,就好像它是一个对象 A”——这是完全合法的,因为继承意味着“按原样” "关系。

所以问题是,您的示例是否可以视为违反了基类规定的契约?好像是的,可以。

查看 this question 的答案其他解释。

附言

不要误会我的意思,所有这些并不意味着您不应该使用私有(private)虚函数。相反,它通常被认为是一种好的做法,请参阅 this thread .但是它们应该从最基类是私有(private)的。所以,底线是,你不应该使用私有(private)虚函数来破坏公共(public)契约。

附言...除非您故意要强制客户端通过指向接口(interface)/基类的指针使用您的类。但是有更好的方法,我相信对这些的讨论超出了这个问题的范围。

关于c++ - 为什么覆盖虚函数时不考虑访问限定符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45671799/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com