0

假设我有一辆车,有引擎和收音机。我想在发动机启动时启动收音机,并且想在发动机超过一定温度时摧毁汽车。在Thinking in C++一书中,有一个组合示例帮助了我。我的想法是相似的:

class Engine
{
public:
    int temperature;
    void start()
    {
        temperature = 15
    }

    void rev()
    {
        temperature += 2;
    }
}

class Radio
{
public:
    void open() {}
}

class Car
{
public:
    ~Car();
    Engine engine;
    Radio radio;
}

我知道我可以手动实现:

main()
{
    Car car;
    car.engine.start();
    car.radio.open();
    while(car != NULL)
    {
        car.engine.rev();
        if(car.engine.temperature > 50)
        {
            delete(&car);
        }
    }
}

我可能有语法错误,这并不重要。我的问题是:如何自动创建类之间的所有通信,以便

main()
{
    Car car;
    while(car!= NULL)
    {
        car.engine.rev();
    }

做同样的工作?我还尝试了继承和虚函数来创建通信,但失败了。

4

5 回答 5

3

你似乎混淆了几个问题。首先,你说你想从delete一个类自身内部。这通常不是一个好主意。

这里的关键是分配和释放内存与拥有有效对象之间存在差异。你可以在你的类中有一个变量,当过热时bool overheated你将设置它。然后你的循环看起来像这样:trueCar

class Engine {
private:
    bool overheated;
public:
    bool isOverheated() const { return overheated; }
    void rev();

    // Rest of implementation ...
};

void Engine::rev() {
    temperature += 2;
    if (temperature > 50) overheated = true; 
}

class Car { /* ... */ };

int main() {
    Car car;
    while (!car.engine.isOverheated()) {
        car.engine.rev();
    }
}

使用NULL,delete等等与内存分配有关。对于这样的类,您通常应该分离关注点。内存分配(堆栈与堆)是一回事,对象是否处于正确状态是另一回事。

编辑:

如果您希望Car's 的状态依赖于Engine's,那么只需这样做:

class Car {
    Engine engine;
public:
    bool isWorking() const { return !engine.isOverheated(); }
};
于 2013-07-05T07:48:55.113 回答
2

首先

你的方法有很多错误的地方,

最重要的(我认为):您似乎混淆了指向动态分配对象和本地声明对象的指针的概念。在您的代码Car car;中不是指针。因此它不能NULL,你不能删除它。

我举个例子给你看

int main()
{
  // this is a pointer unitialized, so it points a random place in memory
  // (depending on what was in memory before) 
  Car * car;
  
  // initialization at NULL
  // the pointer points to NULL, i.e no object
  car = NULL;   

  // a car object is created in memory and the pointer `car` now points on it
  car = new car();

  // do things with your object through the pointer
  car->engine.start();
  
  // the car object is deleted from memory and Car points to NULL
  delete(car);
   
  return 0;
}

现在没有指针(这是在 C++ 中做事的更好方法)

int main()
{
   // car is a local object, it is automatically created by calling the default
   // constructor
   Car car;

   // do things with your object
   car.engine.start();

   return 0;
   
} // <- the oject is automatically destroyed when you go out of the scope

我的示例肯定没有详尽地说明指针和本地对象之间的区别。我强烈建议您阅读有关面向对象编程及其概念的更多信息(例如封装,因为在您的情况下它也不好)。

请记住,C++ 不像其他 OO 语言那样做,例如 java,其中每个 Object 变量都像指针一样工作。

然而

关于您关于对象行为和它们之间关系的原始问题,Lstor 的答案是 OO 设计外观的一个很好的例子。但我认为你对 OO 编程有误解,导致你做出那种糟糕的设计,因此我的建议是阅读(或仔细重新阅读,或选择另一本书)更多关于 C++ 中的 OO 编程的书

于 2013-07-05T08:03:58.293 回答
2

观察者模式

您可以使用观察者模式,它为引擎提供了一种将通知传递给任意相关方的通用方法(通常您会允许多个观察者都收到通知,但为此目的,支持一个就足够了)。

struct IEngineObserver
{
    virtual void on_overheated() = 0;
};

class Engine
{
  public:
    Engine(IEngineObserver& observer) : observer_(observer) { }

    void start()
    {
        temperature_ = 15;
    }
    void rev()
    {
        if ((temperature_ += 2) > 50)
            on_overheated();
    }
    IEngineObserver& observer_;
    int temperature_;
};

class Radio
{
  public:
    void open() {}
};

class Car : public IEngineObserver
{
  public:
    Car() : engine_(this), finito_(false) { }
    ~Car();
    virtual void on_overheat() override { finito_ = true; }
    Engine engine_;
    Radio radio_;
    bool finito_;
};

然后,您可以循环直到car.finito_is truedelete但是,如果您有一些代码在不断地main()调用 Car 对象上的操作……那将崩溃。TheCar将在 结束时超出范围时被销毁main,因此您的重点应该是在正确的时间打破rev()ing 循环 - 即何时finito_设置。

Lambda 和 std::function

可以使用 lambda 和 完成类似的操作std::function,以便 Car 可以指定任意代码让引擎在过热时运行,如...

    Car() : engine_([&] () { finito_ = true; }), finito_(false) { }

    Engine(std::function<void (*)()>& f) : f_(f) { }

...无需额外的IEngineObserver类或on_overheat函数。

于 2013-07-05T08:17:38.757 回答
1

即使在一般情况下不推荐,也可以从其自身中删除对象。你可以这样做:

class A
{
public:
    void commitSuicide()
    { 
        delete this; 
    }
}

但是,在您当前的设计中,有几个问题会阻止您执行此操作:

  1. 您正在使用堆栈中的对象,即不是指向在堆中分配的对象的指针new。因此,禁止调用delete

  2. engine不知道car它属于哪个。因此,在 中engine::rev(),调用delete this;将删除引擎,而不是汽车。一个选项是向 中添加一个成员car * theCarIBelongToengine以便您可以调用delete theCarIBelongTo;。另一种选择是有一个car::revEngine()调用的方法Engine.rev();,然后delete this;在温度太高时调用。(所以你打电话Car->revEngine()而不是Car->Engine.rev()

  3. 最后但同样重要的是,删除对象不会导致指向对象的指针变为NULL,因此您的测试Car != NULL仍然会成功。这将在延迟它时导致未定义的行为(实际上是段错误)。这是最严重的问题,主要是为什么“自杀”往往不被推荐的原因。您需要更改您的设计以将其考虑在内。

因此,即使我的回答是对您问题的最直接回答,我也不建议您在这种情况下使用“自杀”范式。仔细研究提供的其他答案,它们是最有用的,并为您提供了良好的实践。

于 2013-07-05T08:17:50.787 回答
0

“假设我有一辆汽车,有引擎和收音机。我想在引擎启动时启动收音机,并想在引擎超过一定温度时摧毁汽车”

引擎启动时启动收音机:从引擎:

好的,在引擎中添加一个无线电字段并从 engine::start 调用 open 方法

当发动机超过一定温度时摧毁汽车

好的,当您在发动机转速中增加发动机温度时,行后,

temperature += 2;                          /// add this line
if (  temperature > 50  ) car.destroy();

引擎需要有一辆汽车才能摧毁,所以确保你的引擎看起来像这样,

class Engine
{
public:
    int temperature;
    car car;
    void start()
    {
        temperature = 15
    }

    void rev()
    {
        temperature += 2;
        if (  temperature > 50  ) car.destroy();
    }
}



main()
{
    Car car; 
    CAR.ENGINE.CAR = CAR;
    CAR.ENGINE.RADIO = CAR.RADIO;

    while(car!= NULL)
    {
        car.engine.rev();
    }

C'a froid,NON?

于 2013-08-15T23:12:42.763 回答