嵌套类继承自嵌套类。

4
我希望类 C 继承自类 A 的虚函数,而嵌套于类 C 中的类 D 则继承自类 A 中嵌套的类 B 的数据字段。以下是我的代码。
file1.h:
class A{
 public:
     virtual void foo()=0;
     class B{
       public:
         int data;
     };
};

file2.h

class C : public A{
 public:
     class D : public A::B{

     };    
};

file2.cpp

void C::foo(){//code}
C::D::D():data(0){//Bad initialization list, error being data is not a member of C::D even it should inherits from its base class
 data = 0; //good! compiler can see data is a member of A::B,and C::D inherits from it
}

我有两个问题,第一个是我采用的继承方式正确吗?第二个是,正如我所评论的那样,在手动初始化过程中编译器为什么能看到数据来自于A::B而在初始化列表中却看不到呢?它们不应该在同一作用域吗?非常感谢。 编辑1: 所以如果类C::D(foo)没有直接从A::B(foo)继承而来,但是C从A继承,我的理解是,由于C继承了A及其所有public字段,包括其内部类A::B(foo),D(foo)与A::B(foo)完全相同,并且是C的内部类,就像这样,即对两个内部类都使用foo。
class A{
 public:
     class foo{
       public:
         int data;
     };
};

class C : public A{
 public:
     class foo{

     };    
};

如果我直接调用C::foo,编译器会不会感到困惑?因为在作用域中有两个同名的构造函数?或者它会选择调用“最近”的一个,比如C:foo?而不是沿着继承链向上爬?非常感谢。


1
请针对编辑问题提出另一个问题,而不是编辑您的旧问题。首先,被接受的答案仅适用于第一部分。您可以在新问题中引用此问题。 - Kobor42
4个回答

5
  1. Yes, your approach is the correct way to achieve this kind of inheritance.

  2. Initializer lists are there to control the arguments passed to the constructor. You can pass arguments to B's constructor, but not directly initialize a member of B (it's the job of its constructor). If there is no constructor specified for a base class or a member, the default constructor is used. In your case, add a constructor to B to achieve what you want.

    class A {
    public:
        class B{
        public:
            B(int i) : data(i) {}
            int data;
        };
    }; 
    
    class C : public A {
        class D : public A::B {
        };
    };
    
    C::D::D() :B(0) { }
    

3

从语法角度来看,它是正确的(*1)。当然,初始化列表除外。您只能在初始化列表中初始化当前类成员(而不是基类成员)。这与嵌套无关。

class X
{
public: 
   int x;
};
class Y : X
{
   Y() : x(0) {} //still illegal
};

我之所以说从语法角度出发是因为这样做很奇怪...我相信有更简洁的方法来实现你真正想要的目标。


1
在你的例子中,你是不是想让 Y 继承自 X? - Idan

1
从语法上来说,已接受的答案非常好地回答了您的问题。以下是我对机制相关的设计方面的两点看法:
在您的示例中,抽象类A本质上是一个接口。接口应该仅定义行为,而不涉及其他。也就是说,我认为在接口中嵌套类是没有意义的。
1)接口没有任何数据成员可以被嵌套类操作,这与您的示例情况相同。这首先消除了嵌套类的需要。
2)嵌套类不是一种行为。因此,它不应该出现在接口中。
现在你可能会争辩说,你的例子只是一个快速演示,在现实中,你将有数据成员供嵌套类操作,即类A不是一个纯接口。你只想为嵌套类B提供一个默认实现,并让类C决定如何将类B定制为自己的类D。如果是这样,恐怕设计甚至更糟。

3) 考虑迭代器模式。类A本质上是一个抽象容器,不能被实例化(因为它有一个纯虚函数) - 让我们将其映射到我们的IContainer中。现在你创建一个类C,它具有一个向量/数组实现,带有它自己的迭代器D指向数组中的某个元素。

然后你想创建另一个类E,它具有哈希映射实现,带有它自己的迭代器F指向哈希映射中的一些键值对。

这里出现了问题: 在包含类(C,E ...)的实现中,很多时候你无法在迭代器类(D,F ...)之间拥有统一的接口 - 很可能(考虑赋值运算符:BIterator operator =(const BIterator& other);),类D有一个函数接受一个类D对象作为参数:DIterator operator =(const DIterator& other); 类F有一些函数接受一个类F对象作为参数:FIterator operator =(const FIterator& other)

另一方面,这样做行不通,因为DIterator和FIterator都是BIterator的子类——您无法使用“更窄”(或“更具体”)参数覆盖函数,因为这违反了参数类型的逆变性(C ++不允许逆变参数,因此需要匹配参数类型;因此,在类F中可以有,并从BIterator运行时动态转换为FIterator - 但这很丑陋,而且没有意义:具体的苹果具有将抽象的“水果”分配给自己的赋值运算符)。您只是陷入了困境。
总之,由于原因1)2)和3),请勿在接口/抽象基类中包含嵌套类。迭代器模式只是一个例子,只要嵌套类与包含类交互,就可能会发生许多场合的问题。如果没有,则首先没有必要使用嵌套类。

1
您从嵌套类继承的语法是正确的。
初始化列表用于初始化该类的成员;基类成员应在基类构造函数中初始化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接