1

このようなダウンキャストが機能しないことはわかっています。うまくいく方法が必要です。ここに私の問題があります: 基本クラスから派生したいくつかの異なるクラスがあります。私の最初の試みは、基本クラスの配列を作成することでした。プログラムは、異なる派生クラスを (多かれ少なかれランダムに) 選択する必要があります。基本クラスから派生クラスにキャストして、基本の配列に入れようとしましたが、明らかにうまくいきませんでした。かなりの数の派生クラスが存在する可能性があるため、すべての派生クラスの配列を単に貼り付ける以外の方法を心から望んでいました。私が頭を悩ませているだけで、これを行うためのより良い方法はありますか?

コード例や詳細情報が必要な場合は、お知らせください。それはすべて私には理にかなっていますが、それは遅く、他の人には意味がないかもしれません.

皆さん、どんな助けも大歓迎です。

4

9 に答える 9

13

よく分からない。オブジェクトを値で格納しているように聞こえますが、配列はBase. Derived を割り当てるとすぐに、そのオブジェクトが Base に変換され、オブジェクトの Derived 部分がスライスされるため、これは機能しません。しかし、ベースへのポインターの配列が必要だと思います:

Base * bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i] = new Derived1;
    else if(r == 1)
        bases[i] = new Derived2;
    // ...
}

ポインターを扱ったことがある場合は、それらを管理し、特にメモリを解放し、オブジェクトのデストラクタを呼び出すためにそれらに対して delete を呼び出す必要があるため、ポインターを失いません。あなたは shared_ptr を使うことができ、あなたのためにそれを管理します:

shared_ptr<Base> bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i].reset(new Derived1);
    else if(r == 1)
        bases[i].reset(new Derived2);
    // ...
}

bases[x]これで、別の shared_ptr に渡すことができ、複数の参照があることがわかります。オブジェクトへの最後の参照がスコープ外になると、自動的に削除が呼び出されます。理想的には、生の配列も std::vector に置き換えます。

std::vector< shared_ptr<Base> > bases;
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases.push_back(shared_ptr<Base>(new Derived1));
    else if(r == 1)
        bases.push_back(shared_ptr<Base>(new Derived2));
    // ...
}

次に、ベクトルを渡し、そのサイズを失わずに、必要に応じてアイテムを動的に追加できます。を使用してベクトルのサイズを取得しbases.size()ます。shared_ptr ここについて読んでください。

基本クラスから派生クラスへの変換は、絶対に必要な場合にのみ行う必要があります。通常、 と呼ばれる手法を使用しますpolymorphism。これは、ベース ポインターで関数を呼び出すことを意味しますが、実際には派生クラスで定義された関数を呼び出し、同じシグネチャ (名前とパラメーターは同じ型) を持ち、overrideそれに。それについてはウィキペディアの記事を読んでください。本当にキャストする必要がある場合は、生のポインターに対して次のように行うことができます。

Derived1 * d = &dynamic_cast<Derived1&>(*bases[x]);

dynamic_cast を使用すると、間違った型にキャストした場合 (つまり、キャストした型が作成されてベース ポインターに割り当てられた型ではない)、オペレーターによって例外がスローされます。shared_ptr の場合、次の方法もあります。

shared_ptr<Derived1> d = dynamic_pointer_cast<Derived1>(bases[x]);
if(d) {
    // conversion successful, it pointed to a derived. d and bases[x] point still 
    // to the same object, thus share it. 
}
于 2009-01-05T05:47:56.090 に答える
10

基本クラスから派生クラスへのキャストは、ほとんどの場合、悪臭を放ちます。

仮想メソッドを使用したポリモーフィズムは、より優れたアプローチです。

呼び出しコードは、派生クラスの適切な実装にディスパッチできるように、基本クラス ポインターを使用してメソッドを呼び出す必要があります。

これはかなり一般的な回答であることは承知していますが、例としていくつかのコード スニペットがなければ、状況により具体的な回答を推奨することは困難です。

于 2009-01-05T05:44:29.200 に答える
1

私はあなたが何をしようとしているのかまだわかりません。一般にオブジェクトをダウンキャストすることはできませんが、オブジェクトへのポインタをダウンキャストすることはできません。これを行う通常の方法は、dynamic_cast(foo)のようなものです。ここで、fooはbankAccount*タイプです。これにより、MoneyMarketオブジェクトへのポインターまたはnullポインターが提供されます。

ただし、オブジェクトに対して操作を行うためのダウンキャストは通常​​、間違いです。これは通常、仮想継承とポリモーフィズムによってより適切に行われます。これは、オブジェクトをさまざまなタイプに分類する方法です。

利息が何であるかを知りたい場合は、bankAccountの仮想関数として利息を定義すると、利息を支払わない口座タイプはゼロを返すことができます。どのアカウントタイプがMoneyMarketであるかを知りたい場合は、おそらくダウンキャストが最適です。

于 2009-01-05T15:52:33.450 に答える
1

の配列でBaseClass*はなく、 の配列を使用しBaseClassます。(または、メモリを管理するスマート ポインター ライブラリを選択します。)

于 2009-01-05T05:42:27.380 に答える
1

もっと情報が必要だと思います。なぜあなたはダウンキャストしていますか?各派生クラスに同じことをしていますか? その場合は、インターフェイスを使用するか、コードを基本クラスに配置する必要があります (またはその両方)。

たとえば、形状 (ベース) の配列があり、その面積を計算したい場合は、次のようにすることができます。

    interface IShape
    {
       double GetArea();
    }

    class Square : IShape
    {
       double GetArea()
       {
          return sideLengh*sideLength;
       }
       ...
    }

    class FunnyShape : IShape
    {
       //do funny stuff
       ...
    } 

    ...

void calcTotalArea(List<IShape> shapes)
{
   double total = 0.0;
   foreach (IShape s in shapes)
   {
      total += s.GetArea();
   }
   return total;
}
于 2009-01-05T05:50:30.967 に答える
0

継承チェーンを上/下にキャストしようとすることは、一般的に単純に間違っています。これを行っている場合は、継承とポリモーフィズムを正しく使用していない可能性があります。アップキャストまたはダウンキャストが必要な場合は、クラスの設計に問題がある可能性が高くなります。

他の回答で述べたように、この種のことを行う方法は、必要なすべてのメソッドを基本クラスで定義することです。派生クラスがメソッドをサポートしていない場合は、ノーオペレーションにするか、エラーを返すようにします。

于 2009-01-05T16:00:50.313 に答える
0

申し訳ありませんが、いくつかのコードと、目的の最終結果に関するもう少しの情報を投稿できたはずです。

これが私の例です:

class bankAccount
{
   int money;
   bankAccount() {};

   int MoneyInAccount() {return money;};
}

class savingsAccount : public bankAccount
{
   int SavingsInterest();
}

class MoneyMarket : public bankAccount
{
    int CalculateAllPastReturns();
}

次に、プログラムに bankAccount[40] の配列がありました (ポインターではありませんでした)。BankAccount を SavingsAccount、MoneyMarket、CheckingAccount などにキャストできるようにしたいと考えていました。それぞれに独自のクラスがあります。そこから、プログラムはさまざまな銀行口座とその一意の情報のコレクションを持つことができます。

もちろん、そのようにキャストしようとするのが悪いことはわかっていますが、どうすればよいかわかりませんでした。

ポインターなどで何かが露骨に欠けていることを望んでいました。 とにかく、それがもう少し具体的であることを願っています。助けてくれてありがとう!

于 2009-01-05T15:05:37.383 に答える
0

bankAccount[40] オブジェクトの配列がある場合は、派生クラスを使用していません。

派生クラスへのキャストは、instanceof 演算子があり、すべて非常に自然であるため、非常に Java っぽいです。C++ では reinterpret_cast() でエミュレートできますが、これはスタイルが悪いと考えられています。

そもそも何をしようとしているのですか?すべてを 1 つのコンテナーに入れますか?

于 2009-01-05T15:27:08.027 に答える
0

ここでの問題は、SavingsAccount オブジェクトや MoneyMarket オブジェクトではなく、実際の BankAccount オブジェクトを配列に格納していることです。これらの場合、上向きのキャストは機能しないことがわかります。明らかに、reinterpret_cast を使用することはできますが、壊れる可能性があります。(SavingAccount に追加のメンバー変数 (利率) があり、基本クラスよりも数バイト大きくなると想像してください)

あなたがすべきことは、BankAccount オブジェクトへのポインターを格納することです。これらは、好きな派生クラスにすることができます - SavingAccount を割り当て、それへのポインターを配列に入れます。これは問題なく動作し、コンパイラは問題なくオブジェクト型を追跡し、これらの派生型の 1 つに必要なすべてのメモリを割り当てました。

貯蓄口座またはマネーマーケット口座を正しく構築し、どこかに保存して、それらを配列に参照したら、dynamic_cast を使用して、配列に保持されているポインターを実際の型に変換できます。これらのポインターのいずれかを変換しようとすると、間違ったタイプのオブジェクトに接続すると、例外が発生します。(明らかに、SavingAccount オブジェクトを保存してから、それが moneyMarket オブジェクトであるかのようにアクセスしたくはありません!)、型が正しい場合にのみ、オブジェクトへの有効なポインターを取得します。これを機能させるには、RTTI を有効にする必要があります (つまり、コンパイラは各オブジェクトの型を追跡します)。

古き良き C スタイルの共用体を使用する以外に、目的を達成する別の方法は、アクセスするすべてのメソッドを基本クラスに配置し、配列に格納した基本ポインターでそれらを直接呼び出すことです。

そして、それをすべて入力した後-litbはほとんど同じことを言いますが、私が持っているよりもはるかに優れています-彼にチェックを入れてください!

于 2009-01-05T15:46:10.233 に答える