将基数转换为派生类

时间:2016-02-20 11:20:38

标签: c++ casting downcast

在下面的代码中,尽管子类的实例被推送到基类的堆栈上,但在检索(顶部操作)一个元素并将其存储在已经发生的类对象中时,编译器正在生成错误。

#include <cstdio>
#include <stack>

using namespace std;

class base{};

class sub : public base{
public:
    int a;
    sub (int A){ a=A;}
};

int main(){
    stack<base> st;

    sub A = sub(10);
    sub B = sub(20);

    st.push(A);
    st.push(B);
    printf("%d\n", (int)st.size());

    sub C = (sub)st.top();

    return 0;
}

错误:

 In function ‘int main()’:  
24:22: error: no matching function for call to ‘sub::sub(base&)’  
24:22: note: candidates are:  
11:2: note: sub::sub(int)  
11:2: note:   no known conversion for argument 1 from ‘base’ to ‘int’  
8:7: note: sub::sub(const sub&)  
8:7: note:   no known conversion for argument 1 from ‘base’ to ‘const sub&’  

请以任何方式建议我将检索到的基类对象强制转换为已解除的类。

2 个答案:

答案 0 :(得分:1)

这是一个错误,当然因为类base的对象和类sub的另一个对象与编译器不同。当您将r值a等同于另一个值b时,您将a的{​​{1}}作为参数调用copy-constructor。默认情况下,如果您未在类声明中实现此功能,C ++将为b中的成员执行b的每个成员副本。但是,如果aa属于不同的类型,那么复制的成员对编译器来说是不明确的,即使一个成员来自另一个成员(因为总有可能会有其他成员) ,从而增加了对象类的大小和改变结构。)

正如Joachim在评论中所说,要使多态性起作用,你需要处理指针或引用。您不能使用引用,因为您不能拥有引用容器。

b

答案 1 :(得分:1)

std::stack这样的标准容器应该存储指向动态分配的子类对象的指向基类的指针。在现代C ++中,建议您使用 std::unique_ptr 自动执行内存管理。

应使用std::unique_ptr创建

std::make_uniquestd::unique_ptr无法复制,只能移动。当您将未命名的临时文件传递给堆栈的push函数时,会自动发生这种情况。

std::unique_ptr(与std::shared_ptr不同)也需要虚拟析构函数。但这应该不是问题,因为你首先使用基类和派生类的原因应该是你有代码不需要知道派生类型但只使用虚函数的基本类型。 (如果您只需要访问basesub的实现,那么无论如何公共继承都是错误的工具。)

这是一个完整的示例(我还修复了代码中的其他一些问题):

#include <stack>
#include <memory>
#include <iostream>

// using namespace std; is bad practice --> removed

class base {
public:
    virtual ~base() {}
};

class sub : public base{
public:
    int a;
    sub (int A) : a(A) {} // initialisation list rather than assignment
};

int main(){
    std::stack<std::unique_ptr<base>> st;

    st.push(std::make_unique<sub>(10));
    st.push(std::make_unique<sub>(20));
    std::cout << st.size() << "\n"; // no more printf

    auto const& C = *(st.top()); // using C++11 auto

    // no return 0 necessary in main
}

现在,C是基本类型的引用。

如果出于某种原因,您确实需要访问基础sub,那么您可以执行以下操作:

auto const& C = dynamic_cast<sub const&>(*(st.top()));
std::cout << C.a << "\n";

这很难看,但是假设一个具体的子类的整个概念通常是设计缺陷,所以代码的丑陋符合概念的丑陋。