为啥具有私有构造函数的类不阻止从此类继承?如何控制哪些类可以从某个基类继承?
Posted
技术标签:
【中文标题】为啥具有私有构造函数的类不阻止从此类继承?如何控制哪些类可以从某个基类继承?【英文标题】:Why doesn't a class having private constructor prevent inheriting from this class? How to control which classes can inherit from a certain base?为什么具有私有构造函数的类不阻止从此类继承?如何控制哪些类可以从某个基类继承? 【发布时间】:2019-08-27 08:10:54 【问题描述】:class B
private:
friend class C;
B() = default;
;
class C : public B ;
class D : public B ;
int main()
C ;
D ;
return 0;
我假设由于只有类C
是B
的朋友,并且B
的构造函数是私有的,那么只有类C
是有效的并且D
不允许实例化B
.但事实并非如此works。我的推理哪里错了,如何实现对允许哪些类继承某个基类的这种控制?
更新:正如 cmets 中的其他人所指出的那样,上面的 sn-p 按照我最初在 C++14 下的预期工作,但不是 C++17。在 main()
中将实例化更改为 C c; D d;
在 C++17 模式下也可以正常工作。
【问题讨论】:
看这个:***.com/questions/32235294/… @Diodacus:那又怎样,尽管在private:
部分中声明了私有构造函数,但将其声明为默认会使其公开?
我收到了您期望的错误:“'D::D(void)': 试图引用已删除的函数”(msvs 2017)
@Stefan:我听到了,但是有两个类在语义上对 B
的子类是有意义的,我正试图在 C++ 中表达/实施这个逻辑约束。跨度>
我从未见过“恰好有两个类对子类 B 具有语义意义,而我正试图在 C++ 中表达/实施这种逻辑约束。”实际上在与未来的长期接触中幸存下来。一些未来的程序员,也许甚至你,会诅咒你没有看到他们明显需要另一个子类。
【参考方案1】:
这是添加到 C++17 的新功能。正在发生的事情是 C
现在被认为是一个聚合。由于它是一个聚合,它不需要构造函数。如果我们查看[dcl.init.aggr]/1,我们会发现聚合是
聚合是一个数组或一个类
没有用户提供的、显式的或继承的构造函数 ([class.ctor]),
没有私有或受保护的非静态数据成员(子句 [class.access]),
没有虚函数,并且
没有虚拟、私有或受保护的基类 ([class.mi])。
[ 注意:聚合初始化不允许访问受保护和私有基类的成员或构造函数。 — 尾注 ]
我们检查了所有这些要点。您没有在C
或D
中声明任何构造函数,所以有项目符号 1。您没有任何数据成员,因此第二个项目符号无关紧要,并且您的基类是公共的,因此第三个项目符号是满意。
在 C++11/14 和 C++17 之间发生的变化是聚合现在可以有基类。你可以看到旧的措辞here,它明确指出不允许使用基类。
我们可以通过检查 trait std::is_aggregate_v
like 来确认这一点
int main()
std::cout << std::is_aggregate_v<C>;
将打印 1。
请注意,由于C
是B
的朋友,您可以使用
C c;
C c1;
C c2 = C();
作为初始化C
的有效方法。因为D
不是B
的朋友,所以唯一有效的是D d;
,因为那是聚合初始化。所有其他表单都尝试默认初始化,但由于D
已删除默认构造函数,因此无法完成。
【讨论】:
我猜想在B::B() = default;
这样的类外写默认构造函数定义将被视为用户提供的构造函数,而在类中默认它被认为是非用户提供的构造函数?
@VTT 这没什么区别。类内部的B() = default
仍然是用户声明的构造函数。
@NathanOliver 你确定吗?我很确定,在 cppcon 的众多演讲之一中,一位讲师解释了差异,尽管它可能应用于其他地方,而不是在这个例子中。找不到链接。
@Fureeish 100% 确定。将构造函数移出类确实会改变对象的初始化方式:***.com/questions/54350114/…
@VioletGiraffe D d2();
是最麻烦的解析,所以你有一个函数,而不是一个对象。【参考方案2】:
来自What is the default access of constructor in c++:
如果类 X 没有用户声明的构造函数,则没有参数的构造函数被隐式声明为默认构造函数。隐式声明的默认构造函数是其类的内联公共成员。
如果类定义没有显式声明复制构造函数,则隐式声明。 [...] 隐式声明的复制/移动构造函数是其类的内联公共成员。
C 和 D 类的构造函数由编译器在内部生成。
顺便说一句:如果你想玩继承,请确保你定义了虚拟析构函数。
【讨论】:
我认为您误解了我的困惑点,尽管您的链接仍然相关。我知道每个C
和D
都有一个默认的公共构造函数,但是D
不应该能够实例化其基类B
的实例,因为后者的构造函数是私有的。
这能回答问题吗?
那么为什么B() = default;
被威胁为“没有用户声明的构造函数”?以上是关于为啥具有私有构造函数的类不阻止从此类继承?如何控制哪些类可以从某个基类继承?的主要内容,如果未能解决你的问题,请参考以下文章