C++中的面向对象设计问题
Object oriented design issue in C++
假设我有一辆汽车,有发动机和收音机。我想在发动机启动时启动收音机,当发动机超过一定温度时摧毁汽车。在用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();
}
做同样的工作?我还尝试了继承和虚拟函数来创建通信,但失败了。
您似乎混淆了几个问题。首先,您说要从类本身中delete
。这通常不是一个好主意。
这里的关键是,分配和释放内存以及拥有一个有效对象是有区别的。类中可以有一个bool overheated
变量,当Car
过热时,该变量将设置为true
。然后你的循环可以看起来像这样:
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
的状态依赖于Engine
的状态,那么就这样做:
class Car {
Engine engine;
public:
bool isWorking() const { return !engine.isOverheated(); }
};
观察者模式
您可以使用Observer模式,它为引擎提供了一种将通知传递给任意感兴趣方的通用方式(通常您会允许多个观察者都收到通知,但为此目的,支持一个就足够了)。
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_
为true
。不过,如果main()
中有一些代码不断调用Car对象上的操作,那么从引擎中delete
调用Car是没有意义的。。。那将崩溃。当Car
在main
结束时超出范围时,它将被销毁,因此您的重点应该是在正确的时间突破rev()
的循环,即设置finito_
时。
Lambda和std::函数
lambdas和std::function
也可以做类似的事情,这样汽车就可以指定任意代码,让发动机在过热时运行,就像在…中一样
Car() : engine_([&] () { finito_ = true; }), finito_(false) { }
Engine(std::function<void (*)()>& f) : f_(f) { }
而不需要额外的CCD_ 18类或CCD_。
首先
,你的方法有很多错误
最重要的是(我认为):您似乎混淆了指向动态分配对象和本地声明对象的指针的概念。在您的代码中,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编程
即使在一般情况下不建议从对象内部删除对象,也有可能。你可以这样做:
class A
{
public:
void commitSuicide()
{
delete this;
}
}
然而,在您当前的设计中,有几个问题阻止您这样做:
您正在使用堆栈中的对象,即不是指向在具有
new
的堆中分配的对象的指针。因此,禁止调用delete
。engine
不知道它所属的car
。因此,在engine::rev()
中,调用delete this;
将删除发动机,而不是汽车。一个选项是将成员car * theCarIBelongTo
添加到engine
,这样您就可以调用delete theCarIBelongTo;
。另一种选择是使用方法car::revEngine()
,该方法调用Engine.rev();
,然后在温度过高时调用delete this;
。(所以你叫Car->revEngine()
而不是Car->Engine.rev()
)最后但同样重要的是,删除对象不会导致指向对象的指针变成
NULL
,因此您的测试Car != NULL
仍然会成功。这将导致延迟时出现未定义的行为(在实践中,这是一个segfault)。这是最严重的问题,主要是为什么"自杀"通常不被推荐的原因。你需要改变你的设计来考虑它。
因此,尽管我的答案是对您问题的最直接的回答,但我不建议您在这种情况下使用"自杀"范式。仔细研究提供的其他答案,它们是最有用的,并为您提供良好的实践。
"假设我有一辆车,有一台发动机和一台收音机。我想在发动机启动时启动收音机,当发动机超过一定温度时摧毁汽车"
发动机启动时启动收音机:来自发动机:
好的,在引擎中添加一个无线字段,并从引擎调用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();
}
是青蛙,不是?
- 由于缺少类模板,NVCC 编译面向对象代码时出现问题
- 是否有一种设计模式或面向对象的基本原则来处理这种共享资源的情况?
- C++初始化包含另一个对象设计问题的对象
- 面向对象的设计选择
- 程序语言和面向对象语言的游戏设计差异
- 创建面向对象的链表时出现编译问题(编译器错误C2664)
- 面向对象的数据设计
- 面向对象设计-将C应用程序转换为C++
- C++面向对象的客户端套接字应用程序设计
- C++面向对象的问题
- 设计模式:可重用的面向对象软件的元素
- 设计面向对象的同步接收器和发射器
- 用C++实现面向对象设计中的内存管理
- C++面向对象的问题
- C++中的面向对象设计问题
- 面向对象设计播放器和AI控制单元.dynamic_cast
- c++面向对象设计
- typeid()面向对象设计备选方案
- 在保持良好的面向对象设计的同时重用代码以提高性能的最佳方法是什么?
- c++面向对象设计受挫