c++ - 点或箭头运算符与范围解析运算符的比较,用于访问基础子对象

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

C++

给定一个基类Base和一个派生类DerivedDerived的构造函数构造的第一件事就是Base子对象。由于它被称为子对象,因此我假定可以通过使用Derived对象上的点运算符从客户端代码像访问其他成员对象一样对其进行访问。我还假定可以通过Derivedthis->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子对象并进行编译吗?
  • Basemyderived.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中进行编译。这是否意味着Basemyderived的数据成员,因为它由myderived和点运算符限定了?如果是这样,那BaseBase类还是Base子对象?
  • 但是myderived.Base.foo()无法编译。对象名称和点运算符在客户端代码中限定了对象成员的AFAIK访问权限。由作用域解析运算符限定的两种事物(而不是对象名称和点运算符)是(a)外部访问属于 namespace 的任何事物,以及(b)静态数据成员的名称和成员函数的名称在其类定义之外定义的定义,在这种情况下,位于Base之前的::是指Base类,而不是任何Base实例。这是否意味着Base中的myderived.Base是 namespace 或引用了该类?
  • 如果是,那么它是命名空间还是对类的引用取决于它是否先附加::,再附加Base成员?
  • 如果对#7的回答是,那么为什么?这似乎与以下逻辑不一致: namespace 对一个变量的封装本身并不能使 namespace 对变量类型的其他实例进行封装或构造。命名空间仅拥有该类型的一个实例-它包含的变量。对于属于类的成员(例如,静态数据成员)也是如此。该类仅拥有该类型的一个实例-它包含的静态数据成员。相反,一个类的同名非静态数据成员与该类的实例一样多。
  • 给定的h()方法Base和一个Derived对象myderivedmyderived.Base::h();在VC12和g++ 4.8中编译。另外,g++ 4.8 can在该语句中接受任意数量的额外Base::,例如myderived.Base::Base::h();。这样的语句似乎暗示BaseBase的成员。但是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++可以。哪个编译器正确(如果有)?
  • 在任何情况下,这是否意味着 namespace 或类可以包含自身?给定全局int变量x,语句::::x;(具有两个范围解析运算符)不会在任何一个编译器中编译,因此我假设全局范围不包含全局范围。
  • 最佳答案

  • 不,您可以拥有一个名为Base的成员,该成员与Base子对象分开。 ::标点符号将名称解析限制为忽略成员对象名称。
  • 参见#1。通常,答案是否定的,因为您会故意让成员别名成为基础,这会让您发疯。但是,在模板中可能会发生这种情况,其中类可能不知道其基础名称。
  • 否。成员子对象和基础子对象都是子对象,但是它们的访问方式不同。
  • 它始终引用该类,并且名称Base本身是从Base继承的。如果您有一个疯狂的成员别名,则需要使用其他对Base的引用,例如 namespace 限定的ID。
  • static_cast< Base & >( derived_obj )
  • 不,::的优先级高于.,因此在Base::foo中查找myderived成员,然后应用函数调用运算符。但是,在(Base::foo)周围不允许使用括号,因为::不是生成子表达式的运算符;这就是为什么我更喜欢将其称为标点符号。
  • 参见#6。 myderived.Base本身不是什么,因为Base组在::之前使用.
  • 对。注意,类不是 namespace 。它们是不同的东西,都碰巧使用相同的作用域表示法。
  • 这似乎是用可能适用于另一种语言的术语解释了C++。例如,在Java中,类是具有自己的数据成员的对象的排序。在C++中,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/

    相关文章:

    python - 在xml中创建具有命名空间的子元素

    c++ - 如何在将 C++ 宏作为参数传递给另一个宏之前展开它

    c++ - 如何在 C++ 中编辑和覆盖文件中的特定位置

    c++ - vim 大括号匹配带有大括号的注释

    java - JPA深度继承注解属性

    c++ - 如何在 C++ 中声明抽象类的 vector 列表?

    c# - 在具有相同命名空间的多个项目的解决方案中找不到命名空间

    c++ - 关于涉及唯一指针的安全操作

    inheritance - 参数多态性与子类型多态性 F#

    java - Java包和C#命名空间的一些比较