2

C++

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

为什么?我知道Base,它本身是Base类的名称,而不是Base对象的名称。但我认为(客户端代码)或(实现代码)前缀Base限定是指基本子对象,因为没有任何前缀限定,是在构造函数初始化程序中引用子对象的方式。请参阅下面的代码,该代码(除了注释掉的代码)适用于 VC12 和 g++ 4.8。extends and的定义声明了一个数据成员,所以我的对象应该包含两个myderived.this->BaseBaseDerivedDerivedBaseDerivedBasemembaseDerivedBase对象。假设成功编译不是任何编译器标准不一致的结果,控制台输出(在注释中)显示两个不同对象的int成员的不同值,这意味着 in的 ctor 初始化程序是指继承的子对象,而引用声明的数据成员对象。In的 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"
}
  1. 同样,不应该myderived.Base;this->Base;唯一地引用继承的Base子对象并编译?

  2. Baseinmyderived.Basethis->Base引用Base子对象或Base类或任何东西?

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

  4. 从 的角度来看Derived,是否只引用了构造函数初始化器Base上下文中的继承子对象,而只引用了 ctor 初始化器之外的类?DerivedBaseDerived

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

  6. 范围解析运算符 in 的使用myderived.Base::foo(),其中foo()的方法是Base,编译。这是否意味着Base是 的数据成员myderived,因为它由myderived点运算符限定?如果是这样,那是BaseBase还是Base子对象?

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

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

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

  10. 给定方法h()和对象,在 VC12 和 g++ 4.8 中编译。此外,g++ 4.8 可以在该语句中采用任意数量的额外s,例如. 这样的说法似乎暗示是. 但 VC12 给. 但是给定object ,VC12 编译得很好,这也意味着 VC12 可以将一个类视为其自身的成员。但这与其无法编译先前的声明相矛盾。此外,VC12 无法编译具有任意数量额外内容的任何版本BaseDerivedmyderivedmyderived.Base::h();Base::myderived.Base::Base::h();BaseBaseerror C3083: '{ctor}': the symbol to the left of a '::' must be a typeBasemybasemybase.Base::h();mybase.Base::h();Base::s(例如mybase.Base::Base::h())的任何版本,但 g++ 可以。哪个编译器是正确的,如果有的话?

  11. 无论如何,这是否意味着命名空间或类可以包含自身?给定一个全局int变量x,该语句::::x;(带有两个范围解析运算符)不会在任一编译器中编译,所以我假设全局范围不包含全局范围。

4

1 回答 1

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

    前缀::本身不是全局名称空间的名称,而只是语法中的一种特殊情况,以弥补其名称的缺失。同样,无论好坏,您都不能声明

    namespace global = :: ; // error: :: alone does not name anything.
    
于 2013-11-27T00:18:19.100 回答