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

Cod*_*cks 2 c++ inheritance namespaces scope-resolution dot-operator

C++

给定基类Base和派生类Derived,构造函数构造的第一件事DerivedBase子对象.由于它被称为子对象,我认为可以通过在对象上使用点运算符从客户端代码访问它,就像任何其他成员对象一样Derived.我还假设它可以通过Derived实现代码访问this->Base.完全由已经初始化后跟分号的对象的名称组成的语句应该编译但是也没有效果.继这一逻辑,给定一个Derived目标myderived,我想:myderived.Base;在客户端代码和this->Base;Derived的实现也不言编译.

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

#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"
}
Run Code Online (Sandbox Code Playgroud)
  1. 再次,不应该myderived.Base;this->Base;唯一地引用继承的Base子对象并编译?

  2. 是否Basemyderived.Basethis->BaseBase子对象或Base在所有类或什么?

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

  4. 从角度来看Derived,Base只是在Derived构造函数初始化程序的上下文中引用了继承的子对象,并且只引用了Base外部Derived的ctor初始化程序的类?

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

  6. 在VC12和g ++ 4.8中使用范围解析运算符myderived.Base::foo(),其中foo()是一种方法Base.这是否意味着它Base是一个数据成员myderived,因为它是由myderiveddot运算符限定的?如果是这样,那么是BaseBase类或Base子对象?

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

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

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

  10. 给定方法h()Base和一个Derived对象myderived,myderived.Base::h();编译在VC12和g ++ 4.8.另外,g ++ 4.8可以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无法编译mybase.Base::h();具有任何数量的额外Base::s(例如mybase.Base::Base::h())的任何版本,但g ++可以.哪个编译器是正确的,如果有的话?

  11. 在任何情况下,这是否意味着命名空间或类可以包含自己?给定一个全局int变量x,该语句::::x;(带有两个作用域解析运算符)不能在任何一个编译器中编译,因此我假设全局作用域不包含全局作用域.

Pot*_*ter 8

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

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

    namespace global = :: ; // error: :: alone does not name anything.
    
    Run Code Online (Sandbox Code Playgroud)