代码之家  ›  专栏  ›  技术社区  ›  seanhodges

在C++中使用抽象类

  •  16
  • seanhodges  · 技术社区  · 17 年前

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

    我有一些关于问题是什么的线索,显然不允许我实例化一个抽象类,我相信我类中的一些代码正在尝试这样做,尽管这不是我的意图。一些研究建议我应该把这个对象作为实现我想要的目标的指针,但是到目前为止我的尝试都失败了,我甚至不确定这就是答案(因此我在这里问)。

    现在我要提交的是我比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 回复  |  直到 13 年前
        1
  •  27
  •   Johannes Schaub - litb    17 年前

    你的问题是你应该在你的函数中接受一个引用。原因是引用实际上没有复制传递的参数。但如果您接受 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;
    

    还要注意的是,你必须编译C++程序。 g++ ,因为它另外将C++标准库链接到代码。

    g++ -o test test.cpp # instead of gcc!
    
        2
  •  5
  •   Eric Petroelje    17 年前

    您所做的工作将在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"
    
        3
  •  3
  •   Dmitry Khalatov    17 年前

    你现在的问题是联系。 对于C++程序,需要添加标准C++库:

    GCC - O试验 -LSTDC++ 测试程序

        4
  •  3
  •   Robert S. Barnes Antoni    17 年前

    我相信这就是你想做的。它根据handle类是否指向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;
    }
    
        5
  •  1
  •   stepancheg    17 年前

    您应该将存储为指针。

    A* instance;
    

    编辑:我以前写过“参考文献”。C++有不同之处。

        6
  •  1
  •   anon    17 年前

    你不必使用指针 如果您放弃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);
    
        7
  •  1
  •   Eddy    14 年前

    我有这个问题,包括 parent.h 之前 iostream :

    错误:

    include "parent.h"
    include <iostream>
    

    正确的:

    include <iostream>
    include "parent.h"
    
        8
  •  1
  •   ijab    13 年前

    Johannes Schaub-Litb是正确的。

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

    所以需要使用&或*。

        9
  •  0
  •   Aiua    17 年前

    必须将指向的指针用作MyClass的成员。

    class MyClass {
    
        public:
    
            void setInstance(A *newInstance) {
                    instance = newInstance;
            }
    
            void doSomething() {
                    instance->action();
            }
    
        private:
    
            A *instance;
    };
    

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

        10
  •  0
  •   anon    17 年前

    当你说

    A instance;
    

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

        11
  •  0
  •   E Dominique    17 年前

    dmitry是正确的,如果使用gcc,则应该使用-lstcc++,但更好的方法是使用 g++ 相反。(相同的语法)。
    另外,您会注意到(我猜如果您添加了-wall),您会收到一个警告,即具有虚拟函数的类没有析构函数,因此一个好主意是向添加(虚拟)析构函数。