web-dev-qa-db-ja.com

C ++での抽象クラスの使用

拡張オブジェクトをパラメーターとして関数に渡すときに抽象クラスを使用しようとしていますが、これまでのところ、いくつかのコンパイラエラーが発生しています。

私は問題が何であるかについていくつかの手がかりを持っています、私は明らかに抽象クラスをインスタンス化することが許可されていません、そしてこれは私の意図ではありませんが、MyClassの一部のコードがこれを実行しようとしていると思います。一部の調査では、目的を達成するためにオブジェクトをポインターとして参照する必要があると示唆されていますが、これまでのところ私の試みは失敗しており、これが答えであるかどうかもわかりません(そのため、ここで質問します)。

C++よりも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;
}
16
seanhodges

あなたの問題は、関数の参照を受け入れる必要があることです。その理由は、参照が実際に渡された引数をコピーしないためです。ただし、参照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は参照先を変更するため、参照にすることはできません-参照はその存続期間全体で1つのオブジェクトのみを参照できますが、ポインタは、別のアドレスを再割り当てするだけで異なることを指すように設定できます。残りの部分はこのようになります

    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!

あなたがやっていることはJavaで動作します。タイプ「A」のパラメーターまたはメンバー変数の宣言は、実際には「Aへのポインター」を意味するためです。C++では、実際にそれを明示する必要があります。それらは2つの異なるものなので:

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

そして宣言では:

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

これがあなたがしようとしていることだと思います。これは、ハンドルクラスが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

今のあなたの問題はリンケージです。 C++プログラムの場合、標準のC++ライブラリを追加する必要があります。

gcc -o test -lstdc ++ test.cpp

3
Dmitry Khalatov

ポインタを使用する必要はありませんセッターを辞任してコンストラクタを使用する場合。これはC++の重要な機能の1つです。コンストラクターの基本初期化子では、ポインターの使用を回避できることがよくあります。

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);
1
anon

Aをポインタとして格納する必要があります。

A* instance;

編集:私は以前に「リファレンス」を書いたことがあります。 C++には違いがあります。

1
stepancheg

iostreamの前にparent.hを含めることでこの問題が発生しました:

違う:

include "parent.h"
include <iostream>

正しい:

include <iostream>
include "parent.h"
1
Eddy

Johannes Schaub-litbは正しいです。

C++では、抽象クラスを関数のパラメーターまたは戻り値の型として使用できません。抽象オブジェクトをインスタンス化することはできません。

したがって、&または*を使用する必要があります。

1
ijab

Dmitryは正しいです。gccを使用する場合は-lstdc ++を使用する必要がありますが、代わりにg++を使用することをお勧めします。 (同じ構文)。
また、(-Wallを追加すると思います)仮想関数を含むクラスにはデストラクタがないという警告が表示されるので、(仮想)デストラクタをAに追加することをお勧めします。上手。

0
E Dominique

MyClassのメンバーとしてAへのポインターを使用する必要があります。

class MyClass {

    public:

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

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

    private:

        A *instance;
};

これを行わない場合、MyClassコンストラクターはAオブジェクトをインスタンス化しようとします(メンバーオブジェクトの場合と同様)。Aは抽象なので、これは不可能です。

0
Aiua

あなたが言う時

A instance;

タイプAの新しいオブジェクトを作成します。ただし、Aは抽象クラスであるとすでに述べているので、これはできません。他のいくつかの人が示したように、ポインタを使用するか、Aを非抽象にする必要があります。

0
anon