【问题标题】:Calling subclass methods from superclass in a vector C++在向量 C++ 中从超类调用子类方法
【发布时间】:2014-09-10 17:13:56
【问题描述】:

我正在尝试将许多子类元素添加到向量中,遍历它们调用被覆盖的方法,并希望它尽可能调用被覆盖的方法。但是我发现它似乎只是在调用超类方法。

我学习了 Java,但不确定它为什么要在 C++ 中这样做。我尝试使用超类的指针向量重写代码并将子类的指针转换为超类。然后通过指针访问它。

理想情况下,我不想将指针列表放入向量中,因为那时我必须手动删除每个指针(我相信?)以阻止内存泄漏,因为我将使用 new 创建对象,因此它们会持续存在方法调用将它们添加到向量中。

有没有更好的方法来做到这一点,或者当不需要父类时,我是否坚持使用指针并在创建的对象上调用 delete?向量最好是类 X 的列表,而不是类 X 的指针列表

我的结构是:

class a { vector vec of class X,
    method to create and add an instance of X into vector vec,
    method to create and add an instance of Y into vector vec }
class X { talk() }
class Y : public X { talk() }

演示我理想中想要做的事情的代码,但仅通过调用超类方法来显示其损坏:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>

class A {
  public:
    virtual void talk() { printf("A\n"); }
};

class B: public A {
  public:
      void talk() { printf("B\n"); }
};

int main(void) {
    std::vector<A> vec;
    std::vector<A*> vec2;
    A a;
    B b;
    a.talk();
    b.talk();

    vec.push_back(a);
    vec.push_back(b);
    vec2.push_back(&a);
    vec2.push_back(&b);

    for(int i = 0; i < vec.size(); i++) {
        vec[i].talk();
        vec2[i]->talk(); //bad but short for example
    }

}

【问题讨论】:

  • 您确实需要指针(可能是智能指针,如std::unique_ptr)才能获得多态行为。见"object slicing"。它在 Java 中工作,因为在那里,MyClass obj 有效地声明了一个指向 MyClass 的指针。
  • talk()A 中不是虚拟的,因此没有方法可以覆盖。编译器将使用静态类型,即A
  • @0x499602D2 此代码的第一个版本中的错误,已修复,添加了虚拟,但仍无法按预期工作。
  • @IgorTandetnik 嗯,有道理,想添加它作为答案吗?还有更多我可以阅读的文学作品吗?

标签: c++ pointers inheritance vector superclass


【解决方案1】:

您应该将方法声明为virtual,以便能够在子类中覆盖它们。将析构函数设为虚拟也是一个好习惯。

class A {
public:
   virtual void talk() { printf("A\n"); }
   virtual ~A(){}
};

class B: public A {
public:
   // using virtual is not really necessary here, but it's good for clarity.
   virtual void talk() { printf("B\n"); }
};

【讨论】:

  • 那是上传代码的错误,我在上传之前修复了,仍然有输出 AB AA AB
【解决方案2】:

方法应该是virtual
在 java 中方法默认是虚拟的。

class A {
  public:
    virtual void talk() { printf("A\n"); }
};

class B: public A {
  public:
      virtual void talk() override { printf("B\n"); } //override key word is in C++  0x and above
};

【讨论】:

  • 那是上传代码的错误,我在上传之前修复了,仍然有输出 AB AA AB
【解决方案3】:

我认为您缺少的是方法声明中的 virtual 关键字。如果要在调用父类中的方法时访问子类方法,则必须将方法声明为virtual

【讨论】:

  • 那是上传代码的错误,我在上传之前修复了,仍然有输出 AB AA AB
【解决方案4】:

要获得所需的多态行为,您需要将 virtual 说明符添加到要在派生类中覆盖的基类函数中。

class A {
public:
    virtual void talk() { printf("A\n"); }
};

您还应该养成在派生类的重写函数上添加 override 说明符的习惯,以便编译器可以帮助您解决此类问题。

class B: public A {
public:
    virtual void talk() override { printf("B\n"); }
//                      ^ Compiler will report an error if base class' function
//                        is not virtual.
};

您也不能将派生对象分配给基类的实例,或slicing will occur

std::vector<A> vec;
/* ... */
B b;
/* ... */
vec.push_back(b); // Slicing. Information only in B is lost.

Live example using virtual specifier

Live example without virtual specifier

【讨论】:

  • 这不起作用仅供参考,因为我仍然从输出中得到 AB AA AB。
  • 原来代码打错了,在我的IDE上修好了,这里没有,现在修好了。
  • 可能想要删除你答案的第一部分,因为这不是问题(仅仅复制代码就是大声笑)。否则看起来很棒,谢谢:)
  • @Chewett virtual 说明符是多态行为所必需的。没有它,调用talk() 将始终调用A::talk,无论是否从B 的实例调用。 override 说明符不是必需的,但很有用,因为它告诉编译器您要完成什么(虽然它可能会启用优化,但不确定)。
  • @Chewett 虚拟属性将自动转移到覆盖函数,即如果A::talk 是虚拟的,那么B::talk 也将是虚拟的,无论您是否省略了virtual 说明符(它将被秘密地虚拟的)。因此,为了清楚起见,最好将覆盖函数指定为 virtual。
【解决方案5】:

如果你不使用指针,当你将对象复制到向量中时,你会得到“对象切片”。这会将对象简化为向量模板参数中声明的基本类型。所以没有子类,所以没有子类方法可以调用,即使方法是虚拟的。

【讨论】:

    猜你喜欢
    • 2013-03-22
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 2021-12-25
    • 2011-10-24
    • 1970-01-01
    • 2012-04-18
    相关资源
    最近更新 更多