簡體   English   中英

在 C++ 中使用抽象 class

[英]Using an abstract class in C++

在將擴展的 object 作為參數傳遞給 function 時,我正在嘗試使用抽象的 class,但到目前為止我的嘗試導致了一些編譯器錯誤。

我有一些關於問題所在的線索,我顯然不允許實例化抽象 class,我相信 MyClass 中的一些代碼正在嘗試這樣做,即使這不是我的意圖。 一些研究表明我應該參考 object 作為實現我想要的指針,但到目前為止我的嘗試都失敗了,我什至不確定這就是答案(因此我在這里問)。

現在我將提交,我對 Java 比 C++ 更熟悉,我確信我的問題部分是由於這個。

這是我在我的程序中嘗試做的一個例子:

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;
}

您的問題是您應該接受 function 中的引用。 原因是引用實際上並沒有復制傳遞的參數。 但是,如果您接受A - 而不是引用A& - 那么您實際上復制了傳遞給參數 object 的參數,您得到的是A類型的 object - 但實際上這是不允許的!

    // 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;
    }

然后您必須將 class 中的成員更改為指針。 它不能是一個引用,因為setInstance會改變它所引用的內容——一個引用在其整個生命周期內只能引用一個 object,而一個指針可以通過重新分配一個不同的地址來設置指向做不同的事情。 剩下的部分看起來像這樣

    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!

您所做的將在 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"

我相信這就是你想要做的。 它根據句柄 class 是否指向 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;
}

你現在的問題是一個鏈接。 對於 C++ 程序,必須添加標准 C++ 庫:

gcc -o test -lstdc++ test.cpp

Johannes Schaub - litb 是正確的。

在 C++ 中,抽象 class 不能用作函數的參數或返回類型。 我們無法實例化抽象 object。

所以需要使用&或*。

通過在iostream之前包含parent.h我遇到了這個問題:

錯誤的:

include "parent.h"
include <iostream>

正確的:

include <iostream>
include "parent.h"

您應該將 A 存儲為指針。

A* instance;

編輯:我之前寫過“參考”。 C++ 有區別。

如果您辭去 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);

您必須使用指向 A 的指針作為 MyClass 的成員。

class MyClass {

    public:

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

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

    private:

        A *instance;
};

如果您不這樣做,MyClass 構造函數將嘗試實例化一個 A object(就像任何成員對象一樣),這是不可能的,因為 A 是抽象的。

當你說

A instance;

您將創建一個 A 類型的新 object。但是您已經說過 A 是一個抽象 class,所以您不能這樣做。 正如其他幾個人所指出的那樣,您需要使用指針,或者使 A 非抽象。

Dmitry 是正確的,如果你使用 gcc,你應該使用 -lstdc++,但更好的是使用g++ (相同的語法)。
此外,您會注意到(我猜如果您添加 -Wall)您會收到警告,表明您的 class 沒有析構函數,因此最好將(虛擬)析構函數也添加到 A 中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM