在C ++中使用抽象类

时间:2009-03-16 11:53:59

标签: c++ abstract-class parameters

我在将扩展对象作为参数传递给函数时尝试使用抽象类,但到目前为止,我的尝试导致了一些编译器错误。

我有一些关于问题是什么的线索,我显然不允许实例化一个抽象类,我相信MyClass中的一些代码试图这样做,即使这不是我的意图。一些研究表明,我应该将对象作为指向实现我想要的东西,但到目前为止我的尝试都失败了,我甚至不确定这是答案(因此我在这里要求)。

我现在提交我比Java更熟悉Java,我确信我的部分问题是由于这个原因。

以下是我在程序中尝试做的一个示例:

class A {
    public:
        virtual void action() = 0;
};

class B : public A {
    public:
        B() {}

        void action() {
            // Do stuff
        }
};

class MyClass {

    public:

        void setInstance(A newInstance) {
            instance = newInstance;
        }

        void doSomething() {
            instance.action();
        }

    private:

        A instance;
};

int main(int argc, char** argv) {
    MyClass c;
    B myInstance;
    c.setInstance(myInstance);
    c.doSomething();
    return 0;
}

此示例产生与我在程序中获得的编译器错误相同的错误:

sean@SEAN-PC:~/Desktop$ gcc -o test test.cpp
test.cpp:20: error: cannot declare parameter ‘newInstance’ to be of abstract type ‘A’
test.cpp:2: note:   because the following virtual functions are pure within ‘A’:
test.cpp:4: note:   virtual void A::action()
test.cpp:30: error: cannot declare field ‘MyClass::instance’ to be of abstract type ‘A’
test.cpp:2: note:   since type ‘A’ has pure virtual functions
test.cpp: In function ‘int main(int, char**)’:
test.cpp:36: error: cannot allocate an object of abstract type ‘A’
test.cpp:2: note:   since type ‘A’ has pure virtual functions

更新

感谢大家的反馈。

我已经改变了“MyClass :: instance以包含A类型的指针,但我现在得到一些与vtable相关的奇怪错误:

sean@SEAN-PC:~/Desktop$ gcc -o test test.cpp
/tmp/ccoEdRxq.o:(.rodata._ZTI1B[typeinfo for B]+0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
/tmp/ccoEdRxq.o:(.rodata._ZTI1A[typeinfo for A]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
/tmp/ccoEdRxq.o:(.rodata._ZTV1A[vtable for A]+0x8): undefined reference to `__cxa_pure_virtual'
collect2: ld returned 1 exit status

我的修改后的代码如下(A和B尚未修改):

class MyClass {

    public:

        void setInstance(A* newInstance) {
            instance = newInstance;
        }

        void doSomething() {
            instance->action();
        }

    private:

        A* instance;
};

int main(int argc, char** argv) {
    MyClass c;
    B myInstance;
    c.setInstance(&myInstance);
    c.doSomething();
    return 0;
}

11 个答案:

答案 0 :(得分:27)

您的问题是您应该接受函数中的引用。原因是引用实际上并不复制传递的参数。但是,如果您接受A - 而不是引用A& - 那么您实际上复制了传递给参数对象的参数,并且您得到的是A类型的对象 - 但是实际上是不允许的!

    // the reference parameter will reference the actual argument
    void setInstance(A &newInstance) {
            // assign the address of the argument to the pointer member
            // instance. 
            instance = &newInstance;
    }

然后你必须将类中的成员更改为指针。它不能作为引用,因为setInstance将改变它引用的内容 - 引用只能在其整个生命周期中引用一个对象,而指针可以设置为指向不同的事情,只需将其重新分配给不同的地址即可。剩下的部分看起来像这样

    void doSomething() {
        // call a member function on the object pointed to
        // by instance!
        instance->action();
    }

private:

    // a pointer to some object derived from A
    A *instance;

另请注意,您必须使用g++编译C ++程序,因为它还会将C ++标准库链接到您的代码

g++ -o test test.cpp # instead of gcc!

答案 1 :(得分:5)

你正在做什么会在Java中工作,因为声明一个参数或类型为“A”的成员变量实际上意味着“指向A的指针”。在C ++中,你实际上需要明确这一点,因为它们是两个不同的东西:

void setInstance(A* newInstance) { // pointer to an "A"
                instance = newInstance;
}

在宣言中:

A* instance; // Not an actual "A", but a pointer to an "A"

答案 2 :(得分:3)

我相信这是你要做的。它通过实际打印出来的东西来演示多态性,这取决于句柄类是指向B还是C的实例。其他正确的你可能也想要一个虚拟的析构函数。

这可以编译:g ++ test.cpp -o Test

#include <stdio.h>

class A {
    public:
        virtual void action() = 0;
};

class B : public A {
    public:
        B() {}

        void action() {
                printf("Hello World\n");
        }
};

class C : public A {
    public:
        C() {}

        void action() {
                printf("Goodbye World\n");
        }
};

class AHandleClass {

    public:

        void setInstance(A *A_Instance) {
                APointer = A_Instance;
        }

        void doSomething() {
                APointer->action();
        }

    private:

        A *APointer;
};

int main(int argc, char** argv) {
    AHandleClass AHandle;
    B BInstance;
    C CInstance;
    AHandle.setInstance(&BInstance);
    AHandle.doSomething();
    AHandle.setInstance(&CInstance);
    AHandle.doSomething();
    return 0;
}

答案 3 :(得分:3)

现在你的问题是一个联系。 对于C ++程序,必须添加标准C ++库:

gcc -o test -lstdc ++ test.cpp

答案 4 :(得分:1)

您应该将A存储为指针。

A* instance;

编辑:之前我写过“参考”。 C ++有所不同。

答案 5 :(得分:1)

如果您重置了setter并使用构造函数,则不必使用指针。它是C ++中的重要特性之一:构造函数中的基本初始化器通常允许避免使用指针。

class MyClass {

        public:

                MyClass(A & newInstance) : instance(newInstance) {
                }

                void doSomething() {
                        instance.action();
                }

        private:

                A & instance;
};



int main(int argc, char** argv) {
        B myInstance;
        MyClass c(myInstance);

答案 6 :(得分:1)

我在parent.h之前加入iostream来解决这个问题:

错误:

include "parent.h"
include <iostream>

右:

include <iostream>
include "parent.h"

答案 7 :(得分:1)

Johannes Schaub - litb是正确的。

在C ++中,Abstract类不能用作函数的参数或返回类型。我们无法实例化抽象对象。

所以需要使用&amp;或*。

答案 8 :(得分:0)

您必须使用指向A的指针作为MyClass的成员。

class MyClass {

    public:

        void setInstance(A *newInstance) {
                instance = newInstance;
        }

        void doSomething() {
                instance->action();
        }

    private:

        A *instance;
};

如果你不这样做,MyClass构造函数将尝试实例化一个A对象(就像它对任何成员对象一样),这是不可能的,因为A是抽象的。

答案 9 :(得分:0)

当你说

A instance;

你将创建一个类型为A的新对象。但是你已经说过A是一个抽象类,所以你不能这样做。您需要使用指针,正如其他几个人指出的那样,或者使A成为非抽象的。

答案 10 :(得分:0)

Dmitry是正确的,如果你使用gcc你应该使用-lstdc ++,但更好的是使用g++代替。 (语法相同)。
另外,你会注意到(我想如果你添加-Wall)你得到一个警告,你的虚拟函数类没有析构函数,所以一个好主意是向A添加一个(虚拟)析构函数。