Singleton,shared_ptr,原始指针或其他指针?

时间:2013-08-19 02:00:56

标签: c++ design-patterns c++11 shared-ptr

class GameBoy{

  public:
    GameBoy()
      :cart(new Cartridge()),
        mmu(new MMU(cart)),
        cpu(new CPU(mmu))
    {}

    void step(); //steps through one instruction
    const CPU::State &getCPUState() const noexcept;
    void loadROM(std::string fileName);

  private:
    std::shared_ptr<Cartridge> cart;
    std::shared_ptr<MMU> mmu;
    std::shared_ptr<CPU> cpu;

  };

我正在编写一个Game Boy模拟器,这个类就是Qt GUI与之交互的。也就是说,对于GUI中绘制的每个帧,它将调用GameBoy :: step()。显然,由于我还没有开始在GPU上工作,所以还没有完成。

我目前正在质疑这个设计,因为这3个类(Cartridge,CPU,MMU)只会被实例化一次。单身是一个比这更好的设计,还是这个当前的设计是最好的?

如果这个最好,我应该继续使用shared_ptr吗?我已经读到它应该谨慎使用,但在这里,它似乎有意义,因为所有3个类都相互依赖。

非常感谢!

编辑:人们问我为什么要使用指针。从初始列表中可以看出,MMU需要对Cartridge的引用,CPU需要对MMU的引用。

Cartridge将由GameBoy :: loadROM()和大多数MMU方法使用。我同意MMU可能不需要由GameBoy拥有,但它将在CPU和GPU之间共享(我认为)。

在下面的示例中,是否有更好的替代购物车共享指针?

类之间的示例用法:

byte MMU::readByte(word address) {
            ....
            value = cart->readROM(address);
            ....
        }

void GameBoy::loadROM(std::string fileName){
            cart->loadROM(fileName);
}

编辑2:谢谢大家!我有一个最后的问题。如果GPU和CPU都使用MMU并且GameBoy同时使用GPU和CPU,那么设计更好:

class GameBoy{

public:
GameBoy()
 : CPU(&mmu), gpu(&mmu)
...

private:
MMU mmu;  //never used by GameBoy
CPU cpu;  //CPU and GPU used in GameBoy
GPU gpu;

};

class CPU{
...

private:
MMU *mmu;
};

class GPU{

...

private:
MMU *mmu;

};

或:

class GameBoy{

public:
GameBoy()
 : CPU(), gpu(&cpu.getMMU())
...

private:
CPU cpu; //CPU and GPU used in GameBoy
GPU gpu;

};

class CPU{
...

private:
MMU mmu;  //arbitrarily chosen owner.  GPU could have been the owner
};

class GPU{

...

private:
MMU *mmu;

};

我喜欢第一个更好,因为它似乎更一致,但GameBoy从不使用MMU。

4 个答案:

答案 0 :(得分:8)

  

我目前正在质疑这个设计,因为这3个课程   (盒式磁带,CPU,MMU)只会被实例化一次。

没有用户会想要,比如看他们的朋友玩游戏吗?流式传输命令并在主机上模拟它们比流式传输视频更具带宽效率。从来没有人愿意和朋友一起做“链接模式”的事情。从来没有,先生。

也就是说,这是你的应用程序的某种基石,实际上是单身人士的理由,这是完全错误的。您当前的设计和功能并不需要更多,但完全没有理由锁定自己。其次,即使它们只能被实例化一次,永远,这可以通过一些private访问控制有效地实施,并且绝对不需要全局状态。

简而言之,单身人士是现代节目中最糟糕的想法之一。不要使用它。

  

如果这个最好,我应该继续使用shared_ptr吗?我读过这个   它应该谨慎使用,但在这里,它似乎有意义   所有3个班级都相互依赖。

呃,没有。为什么要使用指针?只需将它们作为成员变量直接使用。跨类依赖项没有理由使用shared_ptr,只需使用T *。

答案 1 :(得分:1)

除了使Global对象/变量更加花哨之外,没有什么理由使用Singleton。如果你找到一个好的设计来避免全局变量,那么它很容易让它比单身人士更好。

一些(有争议的)例外是记录器类或对资源的访问,这些资源在系统中可能本质上是全局的,如共享内存。

就shared_ptr与unique_ptr的使用而言。坚持使用unique_ptr,除非你有充分的理由需要shared_ptr。这里它们在功能上是等价的,但unique_ptr清楚地表明Gameboy类拥有三个对象,每个对象各有一个实例。另外,正如其他一些答案所述,您可以完全避免使用指针,但使用它们也有好处。

  

我应该把它放在我的描述中,但正如你从init列表中看到的那样,MMU需要一个指向Cartridge的指针,CPU需要一个指向MMU的指针。使用静态分配的对象有更好的方法吗?

一种方法是抛弃init列表,因为你的构造函数有点复杂并且明确地处理顺序。

GameBoy()
  :cart(nullptr),
    mmu(nullptr),
    cpu(nullptr)
{
    cart = new Cartrige();
    mmu = new MMU(cart);
    cpu = new CPU(mmu);
}

答案 2 :(得分:0)

一个好的设计可以让你同时运行多个仿真,即使这个要求在实践中从未发生过。

我认为您有两种选择,具体取决于依赖项的工作方式。如果mmu需要墨盒,为什么不让墨盒成为mmu的一员呢?而mmu是cpu的成员。或者如果你想让它们在gameboy中都可以访问,我认为这三个人都会和gameboy一样长寿。将它们作为unique_ptr或full-members(没有指针)存储在gameboy中,通过常规指针将它们传递给子类,然后当gameboy被销毁时它们将被删除。

答案 3 :(得分:0)

变化是不变的,您应该确信在您想要更改代码的时候会到来。这意味着如果您当前的设计不是需要单身,那么就没有理由限制自己。当前的实现允许更多的可扩展性。

至于shared_ptr,当您需要指向相同共享实例的多个指针时,可以使用它。例如,假设您有3个类ABCBC都有指向A实例的指针,BC之间共享一个实例,因此只应在之后删除 BC都已删除。在该示例中,您应该使用shared_ptr。否则你应该使用unique_ptr。因此,您不应该使用shared_ptr。实际上,如果cartmmucpu是具体类,而不是接口/基类,则不应使用任何类型的指针。

编辑:

为什么GameBoy类需要CartridgeMMUCPU的实例?为什么不只是CPU创建自己的MMU创建自己的Cartridge