1

私はこのクラスを持っています:

class CComputer {
public:
    // constructor    
    CComputer(string name) {
        this->name = name;
    };
    // overloaded operator << for printing
    friend ostream& operator<<(ostream& os, const CComputer& c);

    // adds some component for this computer
    CComputer & AddComponent(Component const & component) {
        this->listOfComponents.push_back(component);
        return *this;
    };

    // sets address for this computer
    CComputer & AddAddress(const string & address) {
        this->address = address;
        return *this;
    };

    string name;
    string address;
    list<Component> listOfComponents;
};

そして、これらのクラス:

// ancestor for other classes...It's really dummy yet, but I dunno what to add there
class Component {
public:

    Component() {};

    ~Component() {};
};

class CCPU : public Component {
public:

    CCPU(int cores, int freq) {
        this->cores = cores;
        this->freq = freq;
    };

    int cores;
    int freq;
};

class CMemory : public Component {
public:

    CMemory(int mem) {
        this->mem = mem;
    };

    int mem;
};

ここで、CComputer クラスにいくつかの値を入力します。

 CComputer c("test.com");
 c . AddAddress("123.45.678.910") .
     AddComponent(CCPU(8, 2400)) .
     AddComponent(CCPU(8, 1200)).
     AddComponent(CMemory(2000)).
     AddComponent(CMemory(2000)));

そして今、そこに入力したすべての情報(CCPUおよびCMemory詳細を含む)とともに印刷したいと思います

しかし、それを実装する方法、CComputer::listOfComponents実際にアクセスするかどうかを気にせずに反復できるようにするCCPUCMemory?そのリストに追加することはできますが、それらのコンポーネントの変数にアクセスできるようにする方法がまったくわかりません。

したがって、出力は次のようになります。

##### STARTING #####

CComputer:
   name:test.com
   address:123.45.678.910
      CCPU:
         cores:8,freq:2400
      CCPU:
         cores:8, freq:1200
      CMemory:
         mem:2000
      CMemory:
         mem:2000

###### FINISHED! #####
4

3 に答える 3

4

virtual std::string ToString() const = 0;他の人が述べたように、各子クラスによって継承およびオーバーライドされる基本クラスに仮想関数 (例: ) を実装する必要があります。

しかし、それだけでは十分ではありません。コードは、子クラスのインスタンスをリストにコピーするときに発生するスライスを示します。リストにComponentは、関連する子クラスではなく、 type のオブジェクトが含まれます。

あなたがする必要があるのは、ポリモーフィックなインスタンスを保存することです。値自体は決してポリモーフィックではありません。これには (スマート) ポインターまたは参照を使用する必要があります。ただし、参照は標準のコンテナ ( など) に格納できないため、アウトですstd::list。生のポインターを使用することは、最近では悪いスタイルと見なされていますが、クラスの命名規則から判断すると、クラスで最新の C++ を学習していません (申し訳ありません!)。

したがって、生のポインターはおそらく進むべき道です。それに応じてコードを変更します。

  • ポインターのリストを保存します。

    list<Component*> listOfComponents;
    
  • AddComponentの代わりにポインタの引数型を作成しますconst&

  • newed オブジェクトを渡して関数を呼び出します。例:

    AddComponent(new CCPU(8, 2400))
    

これで、コードは左、右、および中央でメモリ リークを起こします。メモリを解放するには、デストラクタを実装する必要があります。

~CComputer() {
    typedef std::list<Component*>::iterator iter_t;
    for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i)
        delete *i;
}

しかし今、あなたのコードは 3 つのルールに違反しています(この記事を読んでください! これは重要であり、このプログラミング クラスで学習する C++ について最も役立つことかもしれません)。したがって、コピー コンストラクターとコピーも実装する必要があります。代入演算子。ただし、できません。ごめん。クラスのコピーを実装するには、クラスに別の仮想関数Component、つまりオブジェクトを複製する仮想関数 ( virtual Component* Clone() const = 0;) を実装する必要があります。そうして初めて先に進むことができます。

での実装例を次に示しますCCPU

Component* Clone() const {
    return new CCPU(cores, freq);
}

…これは、から派生するすべてのクラスで行う必要があります。そうしないと、から派生し、ポインターの背後に隠されてComponentいる型のオブジェクトを正しくコピーできません。Component

これで、CComputerクラスにコピーを実装できます。

CComputer(CComputer const& other)
        : name(name)
        , address(addess) {
    typedef std::list<Component*>::iterator iter_t;
    for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
        listOfComponents.push_back((*i)->Clone());
}

CComputer& operator =(CComputer const& other) {
    if (this == &other)
        return *this;

    name = other.name;
    address = other.address;

    listOfComponents.clear();
    for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
        listOfComponents.push_back((*i)->Clone());

    return *this;
}

このコードは脆く、スレッドセーフではなく、エラーが発生しやすく、有能な C++ プログラマーがこれを作成することはありません1たとえば、実際のコードでは代わりにスマート ポインターを使用しますが、前に述べたように、これはクラスの範囲を超えていると確信しています。


1これは今私を何にしますか?

于 2013-04-22T22:32:21.883 に答える
1

コンポーネントを説明する文字列を返す toString() などと呼ばれるクラス コンポーネントに仮想メソッドを追加するだけです。次に、各コンポーネントが何であるかを正確に気にすることなく、すべてのコンポーネントを反復処理して toString() を呼び出すことができます。これを行うと、コンピューターごとにすべてのコンポーネントの値を出力できます。

ただし、コメントの1つで指摘されているように、質問で指定した出力例は、すべてのコンピューターのCCPUを出力し、次にすべてのコンピューターのすべてのメモリを出力します。出力をそのように並べ替えるには、情報のタイプを表す列挙型または整数を返す getType() など、コンポーネントに別の仮想メソッドを追加する必要があります。次に、2 つの for-next ループを作成し、一方を他方の内側にネストします。外側のループはすべての型を反復し、内側のループは外側のループで指定された型に一致するすべてのコンポーネントで toString() を呼び出すすべてのコンピューターを反復します。ループ用。

この考え方を具現化したものがこちら。

#include <iostream>
#include <string>
#include <list>

using namespace std;

int const TYPE_CCPU = 1;
int const TYPE_MEMORY = 2;

class Component {
public:
    virtual int GetType() { return -1; }
    virtual std::string ToString() const {
        return "OOPS! Default `ToString` called";
    }
};

class CComputer {
public:
    typedef std::list<Component*>::iterator iter_t;

    // constructor    
    CComputer(string name) {
        this->name = name;
    };
    ~CComputer() {
        for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
            delete *i;
        }
    }

    // overloaded operator << for printing
    friend ostream& operator<<(ostream& os, const CComputer& c);

    // adds some component for this computer
    CComputer & AddComponent(Component *component) {
        this->listOfComponents.push_back(component);
        return *this;
    };

    // sets address for this computer
    CComputer & AddAddress(const string & address) {
        this->address = address;
        return *this;
    };

    void PrintType(int type) {
        for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
            if ((*i)->GetType() == type)
                std::cout << (*i)->ToString() << '\n';
        }
    }

    string name;
    string address;
    list<Component*> listOfComponents;
};

class CCPU : public Component {
public:

    CCPU(int cores, int freq) {
        this->cores = cores;
        this->freq = freq;
    };

    int GetType() { return TYPE_CCPU; }

    std::string ToString() const {
        return "CCPU::ToString()";
    }

    int cores;
    int freq;
};

class CMemory : public Component {
public:

    CMemory(int mem) { this->mem = mem; };

    int GetType() { return TYPE_MEMORY; }

    std::string ToString() const {
        return "CMemory::ToString()";
    }

    int mem;
};

typedef std::list<CComputer*>::iterator iter_c;

int main() {
    list<CComputer*> computerlist;
    CComputer *c1 = new CComputer("test.com"), *c2 = new CComputer("test2.com");
    c1->AddAddress("123.45.678.910").
        AddComponent(new CCPU(8, 1200)).
        AddComponent(new CMemory(2000));
    computerlist.push_back(c1);

    c2->AddAddress("987.65.432.10").
        AddComponent(new CCPU(8, 2400)).
        AddComponent(new CMemory(4000));
    computerlist.push_back(c2);

    for(int t=TYPE_CCPU; t<=TYPE_MEMORY; t++) 
        for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
            (*i)->PrintType(t);
    }

    for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
        delete (*i);
    }

}
于 2013-04-22T21:39:17.540 に答える
0

各クラスに ToString() を実装します。.NET では、これは「オブジェクト」型の実装でさえも標準です。

于 2013-04-22T22:00:57.337 に答える