1

基本クラス Student() があり、2 つの派生クラス GradStudent() と UndergradStudent() があります。

基本クラスには getInfo() という関数があり、2 つの派生クラスには独自の getInfo() 関数があります。

割り当ては、学生の配列を作成し、その配列に各クラスのオブジェクトを格納することです。次に、それぞれから getInfo() を呼び出し (それぞれに基本クラス関数のバージョンを使用)、2 つの派生クラスから getInfo() 関数を呼び出します。

私の人生では、これを行う方法がわかりません。これが私がこれまでに持っているものです:

(ここで言及する必要があると思いますが、基本クラスを変更することはできません)

    Student *classRoom[3];

    Student base ( "Jim", "Bean", 1111 );
    classRoom[0] = &base;

    GradStudent test ( "Jack", "Daniels", 1234, 'M', "Art" );
    classRoom[1] = &test;

    UndergradStudent ust1 ( "Johnny", "Walker", 1235, 1 );
    classRoom[2] = &ust1;

    cout << classRoom[1]->getInfo();

誰かが私を正しい方向に向けることができますか? 具体的には配列でなければならないので、ベクトルソリューションはありません(とにかくそれらに慣れていません)。

4

3 に答える 3

2

派生クラスでさえ変更できない場合、それらが特定の型であることを知らずに GetInfo を呼び出すのは困難です。直接的な解決策を使用する必要があるかもしれません:

GradStudent gradStudent;
cout << gradStudent.GetInfo();

または、学生が指定された派生クラスであるかどうかを確認しようとすることもできますが、IMO は非常に醜く危険です (コードを拡張するという点で):

for (int i = 0; i < 3; i++)
{
    GradStudent * gs = dynamic_cast<GradStudent *>(classRoom[i]);
    if (gs != nullptr)
        cout << gs->GetInfo();
    else
    {
        UndergradStudent * ugs = dynamic_cast<UndergradStudent *>(classRoom[i]);
        if (ugs != nullptr)
            cout << ugs->GetInfo();
        else
            cout << classRoom[i]->GetInfo();
    }
}

ポリモーフィズムは、このような状況を処理するために特別に作成されたものであり、GetInfo が基底クラスで仮想化されていないことは、重大なアーキテクチャ上の欠陥であると言わざるを得ません。

さらに: ポインターで遊んでいる場合は、ローカル変数へのアドレスを取得しないでください。それは危険かもしれません。代わりに、メモリの割り当てと割り当て解除を手動で行います。

classRoom[0] = new Student;
classRoom[1] = new GradStudent;
classRoom[2] = new UndergradStudent;

// Process data

for (int i = 0; i < 3; i++)
    delete classRoom[i];

余談: 私は次のようなタスクが嫌いです: Y を使用せずに X を実行します (Y はX を解決するために特別に設計されています)。


編集:(コメントに応じて)

あなたは、仮想機能がどのように問題を解決するのかと尋ねました。少しの間、それStudent::GetInfoが仮想であるとします。次のコードを分析してみましょう。

Student * student = new Student;
student->GetInfo(); // Student::GetInfo is called

GradStudent * gradStudent = new GradStudent;
gradStudent->GetInfo(); // GradStudent::GetInfo is called

今のところ、驚くべきことは何もありません。

Student * student = new GradStudent;
student->GetInfo();
// if Student::GetInfo is virtual, GradStudent::GetInfo is called here
// if Student::GetInfo is not virtual, Student::GetInfo is called here

今、注意深く読んでください。Student::GetInfo 仮想GradStudentでクラスに実装されている場合はGradStudent::GetInfo、実際には変数で呼び出されるにもかかわらず、呼び出されStudentます。ただし、Student::GetInfo が virtualでない場合、前のケースでStudent::GetInfoは が呼び出されます。これは、基本的に仮想関数がどのように機能するかです。

あなたの場合、 Student * 変数の配列があります。それらがStudents、GradStudents、またはUndergradStudents であるかどうかは正確にはわかりません。yourStudent::GetInfoは仮想ではないため、これらの変数でこのメソッドを呼び出すとStudent::GetInfo、実際の型に関係なく、常に が呼び出されます。

そのような場合、私が考えることができる唯一の解決策は、実際にどのクラスであるかを推測しようとすることです:

Student * student = new UndergradStudent;

Student * s = student; // Ok
GradStudent * gs = dynamic_cast<GradStudent *>(student); // Not true, will return NULL/nullptr
UndergradStudent * ugs = dynamic_cast<UndergradStudent *>(student); // Ok

このようにして、元の型に戻りGetInfo、実際の変数型を呼び出すことができます。

ただし、これは醜い解決策であり、この種の問題は仮想関数によって解決されることに注意してください。

C++ FAQは、仮想関数について詳しく読むのに適した場所です。

于 2013-02-14T10:37:27.210 に答える
0

getInfo()メンバー関数が仮想でない場合、割り当ての最初の部分は、配列を反復処理し、getInfo()各インスタンスを呼び出すことです。

2番目の部分では、派生クラスの実際のメンバー関数を呼び出すために、派生クラスのインスタンスを実際の型にダウンキャストする必要があります。

また、次の方法で配列を入力することをお勧めします。

classRoom[0] = new Student( "Jim", "Bean", 1111 );
classRoom[1] = new GradStudent( "Jack", "Daniels", 1234, 'M', "Art" );
classRoom[2] = new UndergradStudent ( "Johnny", "Walker", 1235, 1 );

プログラムの最後にそれらを削除することを忘れないでください。

于 2013-02-14T10:31:30.217 に答える