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;
}

但是现在你的代码违反了三法则(阅读这篇文章!这很重要,它可能是你将在这个编程课程中学习的关于 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() 而不必担心每个组件到底是什么。如果这样做,那么对于每台计算机,您将能够打印出所有组件的值。

但是,正如其中一条评论所指出的,您在问题中给出的示例输出输出所有计算机的 CCPU,然后是所有计算机的所有内存。要像这样对输出进行排序,您需要向 Component 添加另一个名为 getType() 的虚拟方法,它返回一个表示信息类型的枚举或整数。然后,您可以有两个 for-next 循环,一个嵌套在另一个循环中,其中外部循环遍历所有类型,内部循环遍历所有调用 toString() 的计算机,所有组件与外部指定的类型匹配for 循环。

这是实现这个想法的东西。

#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 回答