基类矢量填充了子类不起作用



我试图拥有一个具有共同基类的不同子类指针的向量。向量设置为基类指针,但添加到向量的任何内容都无法获得其是子类的完整功能。可以在错误日志中看到它被视为基类,因此无法获得扩展功能。

我看了很多问题,人们说要按照我的方式做,但是无论出于何种原因,它都行不通。

代码在公共回购上:

https://repl.it/@cubingminer8/inheritance-with-vectors-testing

任何帮助将不胜感激!

编辑:好的,所以我将在C SDL2游戏引擎中使用它用于Sprite组系统。将会有一个基本的精灵级别具有一些基本的东西,例如渲染和移动,而我需要的任何精灵都将是他们自己的班级,它们从Sprite继承,它们将具有自己的独特行为,因此虚拟功能将是不切实际的。将会有一个精灵组对象,可以存储从Sprite继承的对象。因此,它们都可以立即渲染。

如果您曾经使用过 pygame ,那么它几乎与Sprite和SpriteGroup系统相同。https://www.pygame.org/docs/tut/spriteintro.html

#include <iostream>
#include <vector>
class Base {
public:
    char A = 'A';
};
class Sub : public Base {
public:
    char B = 'B';
};
class Sub2 : public Base {
public:
    char C = 'C';
};
int main() {
    std::vector<Base*> List;
    List.push_back(new Sub());
    List.push_back(new Sub2());
    std::cout << List[0]->B << std::endl; // it should be able to print B
    std::cout << List[1]->C << std::endl; // but it is being set as a base class and
                                          // not getting the functionality of the subclass it is.
}

通常,这是通过虚拟函数实现的。在给定的情况下,它应该是一个虚拟getter函数,它返回每个类的char成员。

class Base {
    char A = 'A';
public:
    virtual char getChar()const /*noexcept*/ { return A; }
    virtual Base () = default;
};
class Sub : public Base {
    char B = 'B';
public:
    char getChar()const /*noexcept*/ override { return B; }
};
class Sub2 : public Base {
    char C = 'C';
public:
    char getChar()const /*noexcept*/ override { return C; }
};

现在在主机

std::cout << List[0]->getChar() << std::endl;

作为旁注,我建议您看看智能指针,而不是行指针,您可以避免手动记忆管理。

一个好的开始是:

#include <memory>
std::vector<std::unique_ptr<Base>> List;
List.emplace_back(std::make_unique<Sub>());

,所以您希望它能起作用:

// your code version 1
std::cout<< List[0]->B << std::endl; //it should be able to print B            
std::cout<< List[1]->C << std::endl; //but it is being set as a base class 

但是如果您写这篇文章应该会发生什么?

// your code version 2
std::cout<< List[0]->C << std::endl;
std::cout<< List[1]->B << std::endl;

List[0]没有任何CList[1]没有任何B。您如何建议处理此代码?

有几种方法可以解决此问题。

  1. 编译器应该在编译时间中知道版本1是正确的,而版本2是错误的。不幸的是,这通常是不可能的,因为编译器无法跟踪对象指针在数组中的哪个插槽。因此必须驳回这一点。
  2. 运行时间系统应在运行时间上检测错误。这是一种可能的方法,而不是C 采用的方法。C 是A 静态键入语言。动态键入的语言可以处理这种情况。如果您想要动态键入的语言,请尝试例如python。
  3. 编译器不应试图检测任何内容,并且运行时系统也不应尝试检测任何内容,而是继续进行操作,然后让它产生错误的结果或崩溃。这也是一种可能的方法,但不是任何现代高级编程语言所采用的方法。C 和其他现代语言是键入。可以使用reinterpret_cast等来规避C 的类型系统,但这是非常危险的,不建议。
  4. 编译器应将两个版本都视为错误。这就是C 的作用。

正如其他人提到的那样,扩展类功能的(仅)正确的方法是通过虚拟函数。这需要提前一些计划。基础至少应声明需要哪个操作,尽管它不需要知道派生的类将如何实现它们。

最新更新