【发布时间】:2013-12-12 09:13:51
【问题描述】:
C++
给定一个基类Base和一个派生类Derived,Derived的构造函数首先构造的是Base子对象。由于它被称为子对象,我假设它可以像任何其他成员对象一样通过在Derived 对象上使用点运算符从客户端代码中访问。我还假设它可以通过this->Base 从Derived 的实现代码中访问。完全由已初始化的对象名称后跟分号组成的语句应该可以编译,但也没有效果。按照这个逻辑,给定一个 Derived 对象 myderived,我尝试了:在客户端代码中使用 myderived.Base;,在 Derived 的实现中使用 this->Base;,这两个语句都无法编译。
为什么?我知道Base 本身是Base 类的名称,而不是Base 对象的名称。但我认为Base 由myderived.(客户端代码)或this->(实现代码)前缀限定是指基本子对象,因为Base,没有任何前缀限定,是Base 子对象的引用方式在Derived 的构造函数初始化器中。请参阅下面的代码,该代码(除了注释掉的代码)适用于 VC12 和 g++ 4.8。 Derived 扩展了Base 和Derived 的定义声明了一个Base 数据成员membase,所以我的Derived 对象应该包含两个Base 对象。假设成功编译不是任何编译器标准不符合的结果,控制台输出(在 cmets 中)显示两个不同 Base 对象的 int 成员 n 的不同值,这意味着在Derived 的ctor 初始化器中,Base 指的是继承的Base 子对象,而membase 指的是声明的数据成员对象。在 Derived 的 ctor 初始化器中,Base 专门引用继承的子对象,而不仅仅是任何 Base 对象或 Base 类。
#include <iostream>
struct Base {
Base(int par) : n(par) {}
void foo() { std::cout << "Base: " << n << std::endl; }
int n;
};
struct Derived : Base {
Derived() : Base(2), membase(3) {}
Base membase;
void foo() { std::cout << "Derived: " << n << std::endl; }
// void g() { this->Base; } // Error in VC12 & g++ 4.8
// ^ VC12: error C2273: 'function-style cast' : illegal as
// right side of '->' operator
};
int main() {
Derived myderived;
// myderived.Base; //Error in VC12 & g++ 4.8
// ^ VC12: error C2274: 'function-style cast' : illegal as
// right side of '.' operator
myderived.foo(); // OUTPUT: "Derived: 2"
myderived.Base::foo(); // OUTPUT: "Base: 2"
myderived.membase.foo(); // OUTPUT: "Base: 3"
}
同样,
myderived.Base;或this->Base;不应该唯一引用继承的Base子对象并编译吗?myderived.Base或this->Base中的Base是否指代Base子对象或Base类或任何东西?一般来说,继承的基子对象是否被视为派生类的数据成员?
从
Derived的角度来看,Base是否只引用Derived的构造函数初始化器上下文内的继承子对象,而只引用Derived之外的Base类ctor 初始化器?如何通过
Derived对象访问继承的Base子对象,如Derived的“Derived对象的继承Base子对象”实现代码和客户端代码?在
myderived.Base::foo()中使用范围解析运算符,其中foo()是Base的一个方法,在VC12和g++ 4.8中编译。这是否意味着Base是myderived的数据成员,因为它由myderived和点运算符限定?如果是这样,那么Base是Base类还是Base子对象?但
myderived.Base.foo()无法编译。对象成员的 AFAIK 访问在客户端代码中由对象名称和点运算符限定。由范围解析运算符而不是对象名称和点运算符限定的两种事物是(a)对属于命名空间的任何事物的外部访问和(b)静态数据成员的名称和成员函数的名称在其类定义之外定义的定义,在这种情况下,::之前的Base指的是Base类,而不是任何Base实例。这是否意味着myderived.Base中的Base是一个命名空间或引用该类?如果是,那么它是命名空间还是引用类取决于它是否附加了
::,后跟Base的成员?如果#7 的答案是肯定的,那为什么?这似乎与以下逻辑不协调:命名空间对一个变量的封装本身并不使命名空间能够封装或构造该变量类型的其他实例。命名空间只拥有该类型的一个实例——它包含的变量。作为类的一部分的成员也是如此,例如静态数据成员。该类只拥有该类型的一个实例——它包含的静态数据成员。相反,一个类的同名非静态数据成员与该类的实例一样多。
给定方法
h()的Base和Derived对象myderived,myderived.Base::h();在 VC12 和 g++ 4.8 中编译。此外,g++ 4.8 can 在该语句中采用任意数量的额外Base::s,例如myderived.Base::Base::h();。这样的声明似乎暗示Base是Base的成员。但是 VC12 给出了error C3083: '{ctor}': the symbol to the left of a '::' must be a type。但是给定Base对象mybase,VC12 编译mybase.Base::h();就好了,这也意味着VC12 可以将一个类视为其自身的成员。但这与其无法编译先前的声明相矛盾。此外,VC12 无法编译具有任意数量额外Base::s(例如mybase.Base::Base::h())的任何版本的mybase.Base::h();,但 g++ 可以。如果有的话,哪个编译器是正确的?无论如何,这是否意味着命名空间或类可以包含自身?给定一个全局
int变量x,语句::::x;(带有两个范围解析运算符)在任一编译器中都不会编译,所以我假设全局范围不包含全局范围。
【问题讨论】:
-
我不“拖钓”任何人的问题,尽管我确实拖钓了一些人的答案,因为在这方面有一些聪明的头脑网站,你永远不能停止学习。如果你发布了它,你这样做的目的不是为了做你现在说没有人(包括你自己)要求的事情吗?即,阅读它?
-
@WhozCraig 嗯,这很长,但有一个中心论点。答案和问题一样流动。
-
@Potatoswatter 我同意,它实际上写得非常好,并且有条不紊地呈现,几乎可以作为分步分析和教程阅读。考虑到(显然)没有要求您这样做,我建议您解决每一点。
-
那么,我可以收回收到的仅有的两个赞成票之一吗?如果我有能力和深入的知识来回答你的长篇大论,我会的。我发现它.. 幽默.. 长度和细节最终是名称查找规则和法规摘要的泛音,如果这是我被指责的,我只能接受。这让我想起了大学时代,当时教授们会说“我只有一个问题……而且它有 27 个子部分”。久违了。相反,我会简单地接受这种令人无法接受的情况,然后简单地说晚安。
标签: c++ inheritance namespaces scope-resolution dot-operator