用于访问基础子对象的点或箭头运算符与范围解析运算符

时间:2013-11-26 23:53:18

标签: c++ inheritance namespaces scope-resolution dot-operator

C ++

给定基类Base和派生类DerivedDerived的构造函数构造的第一件事是Base子对象。由于它被称为子对象,我认为可以通过Derived对象上的点运算符从客户端代码访问它,就像任何其他成员对象一样。我还假设可以通过Derivedthis->Base的实现代码访问它。完全由已经初始化后跟分号的对象的名称组成的语句应该编译但是也没有效果。遵循该逻辑,给定Derived对象myderived,我在客户端代码中尝试了myderived.Base;,在this->Base;的实现中尝试了Derived,并且两个语句都没有编译。 / p>

为什么呢?我知道Base本身就是Base类的名称,而不是Base对象的名称。但我认为由Base(客户端代码)或myderived.(实现代码)前缀限定this->是指基础子对象,因为没有任何前缀限定的Base是在Base的构造函数初始值设定项中引用Derived子对象的方式。请参阅下面的代码,其中(注释掉的代码旁边)适用于VC12和g ++ 4.8。 Derived扩展BaseDerived的定义声明Base数据成员membase,因此我的Derived对象应包含两个Base对象。假设成功编译不是任何编译器 - 标准 - 不符合的结果,则控制台输出(在注释中)显示int成员n对于两个不同{{1}的不同值对象,意味着在Base的ctor初始化器中,Derived指的是继承的Base子对象,而Base指的是声明的数据成员对象。在membase的ctor初始值设定项中,Derived专门指向继承的子对象,而不仅仅指任何Base对象和Base类。

Base
  1. 同样,#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;子对象并进行编译吗?

  2. BaseBase中的myderived.Base是否会引用this->Base子对象或Base类或任何内容?

  3. 一般来说,继承的基础子对象是否被认为是派生类的数据成员?

  4. Base的角度来看,Derived只引用Base构造函数初始化程序的上下文中的继承子对象,并且只引用Derived } class Base的ctor初始化程序?

  5. 如何通过Derived对象访问继承的Base子对象,如何,我如何表达“{1}}的继承Derived子对象对象“在Base的实现代码和客户端代码中?

  6. Derived中使用范围解析运算符,其中Derivedmyderived.Base::foo()的方法,在VC12和g ++ 4.8中编译。这是否意味着foo()Base的数据成员,因为它由Base和点运算符限定?如果是,那么myderivedmyderived类或Base子对象是什么?

  7. Base无法编译。对象成员的AFAIK访问权限由客户端代码通过对象名称和点运算符限定。由范围解析运算符限定的两种东西,而不是对象名称和点运算符,是(a)对属于命名空间的任何东西的外部访问,以及(b)静态数据成员的名称和成员函数的名称在其类定义之外定义的定义,在这种情况下,Base之前的myderived.Base.foo()引用Base类,而不是任何::实例。这是否意味着Base中的Base是命名空间还是引用了类?

  8. 如果是这样,那么它是一个命名空间还是引用该类的条件是它是否附加Base后跟myderived.Base的成员?

  9. 如果对#7的答案是肯定的,为什么呢?这与以下逻辑似乎不协调:命名空间的一个变量的封闭本身不能使命名空间包含或构造变量类型的其他实例。命名空间只拥有该类型的一个实例 - 它包含的变量。对于属于类的成员,例如静态数据成员,也是如此。该类只拥有该类型的一个实例 - 它包含的静态数据成员。相比之下,一个类的同名非静态数据成员与该类的实例一样多。

  10. 鉴于::Base对象h()的方法BaseDerived在VC12和g ++ 4.8中编译。另外,g++ 4.8 can在该语句中采用任意数量的额外myderived,例如myderived.Base::h();。这样的陈述似乎意味着Base::myderived.Base::Base::h();的成员。但VC12给出了Base。但是给定Base对象error C3083: '{ctor}': the symbol to the left of a '::' must be a type,VC12编译Base就好了,这也意味着VC12可以将一个类视为自己的成员。但这与其无法编制先前的陈述相矛盾。此外,VC12无法编译任何版本的mybase具有任意数量的额外mybase.Base::h(); s(例如mybase.Base::h();),但g ++可以。哪个编译器是正确的,如果有的话?

  11. 在任何情况下,这是否意味着命名空间或类可以包含自身?给定全局Base::变量mybase.Base::Base::h(),语句int(带有两个范围解析运算符)不能在任何一个编译器中编译,所以我假设全局范围不包含全球范围。

1 个答案:

答案 0 :(得分:8)

  1. 不,您可以拥有一个名为Base的成员,该成员与Base子对象分开。 ::标点符号限制名称解析以忽略成员对象名称。
  2. 见#1。通常答案是否定的,因为你有一个成员别名是有目的的疯狂。但是,它可能发生在模板中,类可能不知道其基础的名称。
  3. 否。成员子对象和基础子对象都是子对象,但访问方式不同。
  4. 它始终引用类,名称Base本身继承自Base。如果你有一个疯狂的成员别名,那么你需要使用Base的其他引用,例如命名空间限定的id。
  5. static_cast< Base & >( derived_obj )
  6. 不,::的优先级高于.,因此在Base::foo内查找myderived成员,然后应用函数调用运算符。但是(Base::foo)周围不允许使用parens,因为::不是生成子表达式的运算符;这就是为什么我更喜欢把它称为标点符号。
  7. 见#6。 myderived.Base本身并不是任何事情,因为在::之前.的基本群组。
  8. 右键。请注意,类不是名称空间;它们是不同的东西,它们都恰好使用相同的范围表示法。
  9. 这似乎是用可能适用于另一种语言的C ++来解释的。例如,在Java中,类是具有自己的数据成员的对象。在C ++中static类成员和名称空间成员是完全独立的对象,可以在任何地方定义。
  10. Base::Base::Base::有效,因为类名称被注入其自身,就像它是成员typedef一样。 VC可能会出错并将其解释为对构造函数的引用。根据规范,特殊的typedef(称为 inject-class-name )是指特殊情况下的构造函数,但是在作用域运算符 not 这种情况之前。< / LI>
  11. 每个类都包含一个隐式typedef。同样,名称空间和类是完全不同的东西。

    前缀::本身不是全局命名空间的名称,而只是语法中的一个特殊情况,用于补偿其缺少名称。同样,无论好坏,你都不能声明

    namespace global = :: ; // error: :: alone does not name anything.