使用类作为BaseClass

时间:2012-05-06 12:25:36

标签: c++

我有:

class BASE{
public:
    virtual void func1() = 0;
};

然后我有一些派生类,比如:

class Derived1 : public BASE{
public:
    void func1() {...implementation....}
};

class Derived2 : public BASE{
    public:
        void func1() {...implementation....}
    };

在主要内容我想做(伪代码):

if ( cond ){
   BASE b := new Derived1
}
else{
   BASE b := new Derived2
}
b.func1();

以便调用的func1()函数是专门用于派生类的函数。

我试着这样做:

BASE b = new Derived1();

但编译器抱怨。

是否可以在C ++中实现?怎么样?

7 个答案:

答案 0 :(得分:4)

使用指针(或引用),否则您的对象将被复制到普通的BASE实例中:

BASE* b = new Derived1;
b->func1();

// later...
delete b;

另外,不要忘记在这种情况下使析构函数为虚拟。

由于整个新/删除事情只是让多态性发挥作用有点麻烦,人们现在倾向于使用智能指针:

std::shared_ptr<BASE> b=std::make_shared<Derived1>();
b->func1();

答案 1 :(得分:3)

显然你已经习惯了垃圾收集的OO语言,比如Java。要很好地回答这个问题,从这个角度来看这可能是件好事。

当你写点什么

Base b = new Derived1();

在Java中,会发生以下情况:

  1. 指向“&b”(我将其称之为β)指向通用Base对象的指针在堆栈上分配
  2. 新的Derived1对象(我称之为d)在堆上分配
  3. β设置为指向Derived对象。
  4. 你在Java中轻易逃脱的原因是有一个垃圾收集器。只要β在堆栈上并且指向d,这就不会产生任何影响,因为GC知道d仍然可以访问并且可能正在使用中。但是,只要没有指针再引用d(例如因为您声明b的函数留下范围),就允许GC释放d占用的空间。容易,但垃圾收集有several disadvantages,我们在C ++中不需要。

    所以在C ++中,如果你做了像

    这样的事情
    Base* b = new Derived1();
    

    直接对应于Java版本,你有一个问题:当b离开作用域时,没有任何内容引用d,但它仍然存在于堆 !您需要自己删除

    delete b;
    

    (请注意,这有一个很大的优势,你可以准确地确定它被删除的位置,而垃圾收集器可能会在很长一段时间内无用地放置它并且仅在你开始耗尽内存时将其擦掉)。但是再一次,这还不够:与垃圾收集器不同,您的程序不会自动知道b指向Derived1对象而不是Base对象,所以{{将删除认为它正在处理delete。但是很容易理解:在Base类中包含一个虚拟析构函数,比如

    Base


    现在,这需要手动删除所有内容显然有点危险:如果你忘记这样做,你就会发生内存泄漏,即只要永远不会从堆中删除对象你的程序运行。出于这个原因,应该使用智能指针而不是普通的C风格指针,这会自动删除它们在离开范围时指向的对象。在C ++ 11中,这些是在标准库(标题class Base{ public: virtual void func1() = 0; virtual ~Base() {} }; )中定义的。你刚才做了

    <memory>

    现在,当std::unique_ptr<Base> b(new Derived1()); 离开作用域时,b对象会自动删除。

    在所有这些示例中,调用虚函数的工作方式相同:

    Derived1

答案 2 :(得分:2)

您正在尝试指定一个指向普通对象的指针。

new Derived1()

该表达式返回一个指针。您要么必须使BASE指针(BASE *),要么只在堆栈上创建它(最好是Base b;)。但在这种情况下,您需要指针(或引用)来在类之间进行转换。

另外,你不能在if-else语句中创建对象,因为它会在你以后想要使用时超出范围。

这是你可以做的事情:

BASE *b;

if (cond)
    b = new Derived1();
else
    b = new Derived2();

b->func1();

当然,你必须记住事后delete你的指针。考虑简单地使用智能指针。

答案 3 :(得分:2)

有两种方法可以做到这一点:

  • 使用类型基类的指针:

    Base *b = new Derived1;

  • 如果要在副本构造上创建派生,请使用类型基类的const引用:

    Base const& b = Derived;

  • 如果要分配在别处创建的派生类对象,请使用类型基类的非const引用:

    Derived d;  Base& b = d;

尼特:

  • 为了继承,您需要在类定义中指定它:

    class Derived1 : public Base {

  • 您的函数需要有一个返回值。

    class B{ public: virtual void func() = 0; };

答案 4 :(得分:1)

你忘记了吗?

class Derived1 : public BASE
{
};

至少我没有在你的代码中看到它 - 派生的BASE类

答案 5 :(得分:1)

已经涵盖了指针(及其陷阱)的使用,因此我将向您展示如何在没有动态内存分配的情况下使用多态,这可能证明我甚至可以考虑它是异端。

首先,让我们看看你的原始代码,修补它以便编译:

void foo(bool const cond) {
    Base* b = 0;
    if (cond) { b = new Derived1(); }
    else      { b = new Derived2(); }

    b->func1();
    delete b; // do not forget to release acquired memory
}
假设virtual具有Base析构函数,

可以正常工作。

程序员发展的下一个合乎逻辑的步骤是使用智能指针来避免编写deletedelete仅供初学者和专家库编写者使用):

void foo(bool const cond) {
    std::unique_ptr<Base> b;
    if (cond) { b.reset(new Derived1()); }
    else      { b.reset(new Derived2()); }

    b->func1();
}

当然,virtual还需要一个Base析构函数。

让我们意识到这个功能做了两件事:

  • 选择给定条件的课程
  • 对生成的实例做一些工作

我们可以将其分解,例如通过提取构建工作:

std::unique_ptr<Base> build(bool const cond) {
    if (cond) { return { new Derived1() }; }
    return { new Derived2() };
}

void foo(bool const cond) {
    std::unique_ptr<Base> b = build(cond);
    b->func1();
}

这是大多数人所做的事。

我声称还有另一种可能性,我们可以隔离实际工作,而不是隔离构建:

void dowork(Base& b) { b.func1(); /* and perhaps other things */ }

void foo(bool const cond) {
    std::unique_ptr<Base> b(cond ? new Derived1() : new Derived2());
    work(*b);
}

我们实际上可以更进一步:

void foo(bool const cond) {
    if (cond) {
        Derived1 d;
        work(d);
    } else {
        Derived2 d;
        work(d);
    }
}

多态性不需要动态内存分配。

你知道最后一个例子的有趣之处:

  • 没有C ++ 11移动语义,它可以完美地运行
  • 它不需要在virtual
  • 中使用Base析构函数

答案 6 :(得分:0)

如果函数签名是相同的,那么在这种情况下接口声明可能更合适...然后让每个类实现接口,并声明变量是你声明的接口类型...