0

私は自分の会社のために構築したプロジェクトのクラス構造をいくつか持っています。ある時点で、派生オブジェクトの「通常は望ましくない」スライスが実際に私のコードを効率的にするのを見てきました。観察してください:

class Base {
  private:
    int myType; //some constant here
  public:
    Base(): myType(1)
    { 
       if(someTest()) 
       { 
         myTest = 0; // signifies that this object is not working well for me
       }  // if the test succeeds, now I know this is a TYPE1 object
    }

    virtual bool someTest() { /* does make some validation tests */ } 
    int GetMyType() { return myType; }

    // some more virtual functions follow
}

class Derived: public Base {
  public:
    Derived() : Base() 
    { 
      if( someTest() /*calls the derived*/ ) 
      { 
        myType =2; // signifies that this is a TYPE2 object
        /* Do something more*/ 
      }  
    }

    virtual bool someTest() { /* does make OTHER validation tests */ } 
}

ここで、Derived の someTest() が失敗した場合、これは、オブジェクトが実際には Base タイプ (myType = 1) または障害のあるオブジェクト (myType =0) であることを示しています。これらはコンストラクターにあり、例外処理 (組み込みシステム) を使用できないため。私はこれについて考えました:

{ 
  // this is the object holder test block
  // there is some latch - Base* - above = let's call it "ObjectHolder"
  extern Base* ObjectHolder;


  Derived * deriv = new Derived();
  int dType = deriv->GetMyType() ;
  if( dType == 1) // if test at base succeded but the one in derived failed
     Base = & static_cast<Base> (*deriv); // specifically slice the object
  else if(dType =2) 
     Base = deriv;
  else
     delete deriv; // this is a TYPE0 - nonworking- object so don't latch it

  // use other implemented methods to do work ...

}

では、なぜこのようなデザインになったのでしょうか。さて、クラスを設計しているうちに、「Base」(type 1) と「Derived」(type 2) のクラスの内部動作のメソッドが異なるため (さらに、type 3,4,5 の可能性がある..)内部も異なるオブジェクト)、およびすべてのメソッドを作成して「if-then-else」でチェックしたくなかったため。私はそのような設計を行い、異なるメソッドを仮想化して、(ポリモーフィズムのおかげで) 正しく呼び出すことができるようにし、共通のメソッドをいくつかの基本クラスに置くことができると考えました。

しかし今、最初に、その奇妙な構文 ( & static_cast < Base > *deriv ) が正しいかどうかはわかりません (テストされ、機能しているように見えましたが、これが運によるものではないことを確認したいだけです)。第二に、これがこれを達成するための「推奨される方法」であるかどうかはわかりません-この構文がメモリリークなどを引き起こす可能性があると思います...

問題をより明確にするために、コードを少し変更しました (構文も修正しました)。

さて、 &(static_cast(*deriv)) は誤ったアプローチであるため、Base クラスでのチェックをバイパスする「コピー コンストラクター」を作成できるかどうかを考えていました (これが、実際にこれらのものを試す理由です。場合によってはそのテストを実行したくない)。以下のように:

class Base { 
    // other stuff is like above

    // cc:
    Base(const Base &other) : myType(other.myType)
    {
         // no test is performed here!
    }

    // like other one above
}

これを書いたので、テストブロックでそれを行うことができると思います:

{ 
   // the code above

   if( dType == 1) // if test at base succeded but the one in derived failed
     Base = new Base(*deriv); // specifically slice the object with cc

    // code goes on ... 
}

これはどうですか?

4

1 に答える 1

2

確かに、2つのタイプのオブジェクトを作成する場合、正しい方法は、「if(sometest)」を含むファクトリ関数を作成してから、正しいオブジェクトタイプを作成することです。

 Base* factory(...) 
 {
     if(sometest())
     {
         return new Derived;
     }
     else
     {
          return new Base;
     }
 }

そうすれば、実際にBaseタイプのオブジェクトが必要なときに、派生タイプのオブジェクトを不必要に作成する必要がなくなります。オブジェクトをスライスした場合、スライスvtable された派生オブジェクトに関連付けられたものは引き続き派生であることに注意してvtableください。

編集:スライスの問題を示すコードは次のとおりです。

#include <iostream>

using namespace std;

class Base
{
public:
    virtual void func() { cout << "Base" << endl; }
};


class Derived : public Base
{
public:
    virtual void func() { cout << "Derived" << endl; }
};


int main()
{
    Base *list[10];
    for(int i = 0; i < 10; i++)
    {
        if (i % 2)
        {
            list[i] = new Base;
        }
        else
        {
            list[i] = new Derived;
        }
    }

    list[2] = &*(static_cast<Base*>(list[2]));

    for(int i = 0; i < 10; i++)
    {
        list[i]->func();
    }
    return 0;
}

出力:

Derived
Base
Derived     <- should have changed to "Base" if questio was "right".
Base
Derived
Base
Derived
Base
Derived
Base

元の質問で与えられた「スライス」の構文を使用すると (ここに投稿されたコードに適合したように)、とコンパイラ&(static_cast<Base>(*list[2]));の両方でエラーが発生します。メッセージの本質は同じです。これはclangの変種です:clang++g++

error: taking the address of a temporary object of type 'Base'
于 2013-01-25T20:36:46.720 に答える