必须使用指针来抽象类导致困难

时间:2011-10-19 09:39:36

标签: c++ pointers abstract-class

我的第一个问题是......

我想使用抽象类A从中派生出一个类B(以及类B2B3,...)。我明白为了统一处理它们,我必须使用指向基类的指针,所以这里的变量类型为A*

在下面的示例中,我想填写函数aavector<A*>类型的f。由于我只能使用引用而变量在f的末尾会失去其范围,因此我无法再访问aamain条目的成员。

什么可以解决这个问题?

#include <vector>
#include <iostream>


class A {
    public:
        virtual int getVar(int, int) = 0;
};

class B : public A {
    public:
        B(std::vector<std::vector<int> > var) : var_(var) {};
        int getVar(int i, int j) { return var_[i][j]; }
    private:
        std::vector<std::vector<int> > var_;
};

void f(std::vector<A*>& aa) {
    std::vector<std::vector<int> > var(1, std::vector<int>(1));
    var[0][0] = 42;

    B b(var);

    aa.push_back(&b);
}

int main() {
    std::vector<A*> aa;

    f(aa);

    std::cout << aa[0]->getVar(0, 0) << std::endl;

    return 0;
}

3 个答案:

答案 0 :(得分:7)

B b(var);
aa.push_back(&b);

它正在推送 local 变量的地址。这就是问题的原因,因为局部变量在从functon返回时被破坏,但是aa仍然包含指向不存在的对象的指针。这样的指针称为dangling pointer,用于调用未定义的行为 - 这是C ++中最危险和最令人恼火的方面之一。

使用new

aa.push_back(new B(var));

现在应该可以了。

重要提示:不要忘记让~A()虚拟:

class A {
    public:
        virtual ~A() {} //MUST DO IT
        virtual int getVar(int, int) = 0;
};

否则,您将无法使用基类型的指针删除派生类型的对象(以明确定义的方式)。

答案 1 :(得分:3)

您观察到的问题是范围问题。

C ++中有两种不同的分配策略:

  • 自动存储:在块({ })内声明的对象,其生命周期以块结束
  • 动态存储:分配有new的对象(如果是数组,则为new[]

如果要创建变量并在其自己的范围之外访问它,则需要依赖动态存储。但是,它并不是免费的,因为那时你会暴露自己的终生管理问题,因为它现在是手动的。

因此,建议使用RAII惯用法将动态分配的对象的生命周期与自动分配的对象的生命周期联系起来。在您的情况下,使用智能指针(或容器)。

使用C ++ 11:

int main() {
  std::vector< std::unique_ptr<A> > aa;
  foo(aa);

  std::cout << aa.front()->getVar(0,0) << "\n";
}

相应地更新foo

void foo(std::vector< std::unique_ptr<A> >& aa) {
  std::vector<std::vector<int> > var(1, std::vector<int>(1));
  var[0][0] = 42;

  aa.push_back(new B(var));
}

答案 2 :(得分:2)

问题出在您的f()功能范围内。退出函数后,您正在创建一个自动分配的对象,该对象不再存在。您必须使用new在堆上分配它。

B* b = new B(var);
aa.push_back(b);

当你不再使用它时,你必须自己释放它。

int main() {
    ...


    for (std::vector<A*>::iterator it = aa.begin(); it != aa.end(); ++it)
    {
        delete *it;
    }
    return 0;
}