C++
给定一个基类Base
和一个派生类Derived
,Derived
的构造函数构造的第一件事就是Base
子对象。由于它被称为子对象,因此我假定可以通过使用Derived
对象上的点运算符从客户端代码像访问其他成员对象一样对其进行访问。我还假定可以通过Derived
从this->Base
的实现代码中访问它。完全由已经初始化的对象名称组成的语句后跟分号,该语句应该可以编译,但无效。按照这种逻辑,我给了一个Derived
对象myderived
,我尝试了:客户端代码中的myderived.Base;
和this->Base;
的实现中的Derived
,这两个语句都不会编译。
为什么?我知道Base
本身就是Base
类的名称,而不是Base
对象的名称。但是我认为以Base
(客户端代码)或myderived.
(实现代码)前缀限定的this->
是指基础子对象,因为Base
(没有任何前缀限定)是在Base
的构造函数初始化程序中引用Derived
子对象的方式。请参阅以下代码,该代码(不带注释的代码)在VC12和g++ 4.8中有效。 Derived
扩展了Base
,并且Derived
的定义声明了Base
数据成员membase
,因此我的Derived
对象应包含两个Base
对象。假设成功的编译不是任何编译器-标准-不合格的结果,则控制台输出(在注释中)显示了两个不同int
对象的n
成员Base
的不同值,这意味着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
子对象并进行编译吗? Base
或myderived.Base
中的this->Base
是否引用Base
子对象或Base
类或其他任何东西? Derived
的 Angular 来看,Base
是否仅引用Derived
的构造函数初始化器上下文中的继承子对象,并且仅引用Base
的ctor初始化器之外的Derived
类? Base
对象访问继承的Derived
子对象,例如,如何在Base
的实现代码和客户端代码中表达“Derived
对象的继承的Derived
子对象”? myderived.Base::foo()
中使用范围解析运算符,其中foo()
是Base
的一种方法,可在VC12和g++ 4.8中进行编译。这是否意味着Base
是myderived
的数据成员,因为它由myderived
和点运算符限定了?如果是这样,那Base
是Base
类还是Base
子对象? myderived.Base.foo()
无法编译。对象名称和点运算符在客户端代码中限定了对象成员的AFAIK访问权限。由作用域解析运算符限定的两种事物(而不是对象名称和点运算符)是(a)外部访问属于 namespace 的任何事物,以及(b)静态数据成员的名称和成员函数的名称在其类定义之外定义的定义,在这种情况下,位于Base
之前的::
是指Base
类,而不是任何Base
实例。这是否意味着Base
中的myderived.Base
是 namespace 或引用了该类? ::
,再附加Base
成员? h()
方法Base
和一个Derived
对象myderived
,myderived.Base::h();
在VC12和g++ 4.8中编译。另外,g++ 4.8 can在该语句中接受任意数量的额外Base::
,例如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无法编译具有任何数量的额外mybase.Base::h();
的Base::
的任何版本(例如mybase.Base::Base::h()
),但是g++可以。哪个编译器正确(如果有)? int
变量x
,语句::::x;
(具有两个范围解析运算符)不会在任何一个编译器中编译,因此我假设全局范围不包含全局范围。 最佳答案
Base
的成员,该成员与Base
子对象分开。 ::
标点符号将名称解析限制为忽略成员对象名称。 Base
本身是从Base
继承的。如果您有一个疯狂的成员别名,则需要使用其他对Base
的引用,例如 namespace 限定的ID。 static_cast< Base & >( derived_obj )
。 ::
的优先级高于.
,因此在Base::foo
中查找myderived
成员,然后应用函数调用运算符。但是,在(Base::foo)
周围不允许使用括号,因为::
不是生成子表达式的运算符;这就是为什么我更喜欢将其称为标点符号。 myderived.Base
本身不是什么,因为Base组在::
之前使用.
。 static
类成员和 namespace 成员是完全独立的对象,可以在任何地方定义。 Base::Base::Base::
之所以有效,是因为将类名作为成员typedef
注入(inject)到自身中。 VC可能会出错,并将其解释为对构造函数的引用。根据规范,特殊的typedef(称为“注入(inject)的类名”)是指在特殊情况下的构造函数,但在作用域运算符不是这种情况之前。 typedef
。同样, namespace 和类是完全不同的东西。前缀
::
本身并不是全局 namespace 的名称,而只是语法中的一种特殊情况,以弥补其名称的不足。同样,无论好坏,您都无法声明namespace global = :: ; // error: :: alone does not name anything.
关于c++ - 点或箭头运算符与范围解析运算符的比较,用于访问基础子对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20230436/