【问题标题】:Initializing member data of pointer type to NULL in constructor that uses Builder Pattern在使用 Builder 模式的构造函数中将指针类型的成员数据初始化为 NULL
【发布时间】:2012-12-17 11:39:46
【问题描述】:

我有一个构造函数,它使用内部Builder 对象来实例化一个复杂对象。数据结构中的五个成员是指针类型。但是,当对象被销毁时,使用这种模式我会遇到问题。以下是我的构造函数的样子,带有成员初始化列表:

Player::Player(const Builder& builder) 
    :m_name(builder._name)
    ,m_description(builder._description)
    ,m_primaryAttributes(builder._primaryAttributes)
    ,m_abilityAttributes(builder._abilityAttributes)
    ,m_armor(builder._armor)
    ,m_weapon(builder._weapon)
    ,m_inventory(new ComponentMap())
{}

客户端代码运行良好,符合预期:

Player* player = Player::Builder()
    .name("Dylan")
    .description("Super bad-ass hero of the game")
    .primaryAttributes(createPrimaryAttributes())
    .abilityAttributes(createAbilityAttributes())
    .weapon(createWeapon())
    .armor(createArmor())
    .build();

但是,如果我在消息链中省略其中一个参数,然后销毁我的 Player 对象,就会发生不好的事情:

Player* player = Player::Builder()
    .name("Dylan")
    .description("Super bad-ass hero of the game")
    .primaryAttributes(createPrimaryAttributes())
    .abilityAttributes(createAbilityAttributes())
    .armor(createArmor())
    .build();

// ...

delete player;

// ...

// cleanMemory() gets called in Player::~Player()
void Player::cleanMemory()
{
    if(m_primaryAttributes != NULL )
        delete m_primaryAttributes;
    if(m_abilityAttributes != NULL )
        delete m_abilityAttributes;
    if(m_inventory != NULL )
        delete m_inventory;
    if(m_weapon != NULL)          // oops, bad stuff happens here
        delete m_weapon;
    if(m_armor != NULL)
        delete m_armor;
}

很明显,这是因为武器的指针没有被初始化为NULLWeapon 对象的实例。如果从链中省略一个Builder 方法,构造函数似乎也不允许默认NULL(至少从我所见)。现在,客户端必须要么给Weapon 一个指向NULL 的指针,要么给一个对象的实例。

有没有办法在不完全修改 Builder 构造函数的情况下解决这个问题?或者,是否应该使用另一种模式(例如Factory)对其进行重构,然后返回到带有位置参数列表的常规构造函数?

【问题讨论】:

  • 请告诉我们Player::Builder函数?
  • 我的第一个想法是智能指针。那将完全消除 cleanMemory()。
  • 我想看看Builder的构造函数。
  • @Mooing Duck 和@billz:Builder 的构造函数只是默认构造函数,但既然你提到了它,我现在看到我可能可以在那里初始化指针类型的值。谢谢。
  • 我很好奇这个调用是怎么可能的,所以我想看看源代码。 Player* player = Player::Builder() .name("Dylan") .description("Super bad-ass hero of the game") .primaryAttributes(createPrimaryAttributes()) .abilityAttributes(createAbilityAttributes()) .weapon(createWeapon()) .armor(createArmor()) .build();

标签: c++ pointers memory-management null builder


【解决方案1】:

你提到的我的示例代码不是很好的代码库,我只是建议下面build pattern

class Builder
{
   Weapon* BuildWeapon() { return new Weapon(); }
   Armor*  BuildArmor(); { return new Armor(); }    
};

class Player
{
public:
  Player(const Builder& builder) 
  : weapon_ptr(builder.BuildWeapon()),
    armer_ptr(builder.BuildArmor())

private:
  std::shared_ptr<Weapon> weapon_ptr;
  std::shared_ptr<Armor>  armor_ptr;
};   

用法:

Builder builder;
std::shared_ptr<Player> player(new Player(builder));

或者你也可以

class Player2
{
public:
  Player() {}
  void SetWeapon(Weapon* p) { weapon_ptr.reset(p); }
  void SetArmor(Armor* p) { armor_ptr.reset(p); }

private:
  std::shared_ptr<Weapon> weapon_ptr;
  std::shared_ptr<Armer>  armer_ptr;
};

用法:

   Builder builder;
   std::shared_ptr<Player> player;
   player->SetWeapon(builder.BuildWeaper());
   player->SetArmor(builder.BuildArmor());

由于 Weapon_ptr、armer_ptr 是智能指针,动态分配的内存不再需要调用delete,因此可以去掉cleanMemory()函数。

这只是一个简单的示例,您可以扩展播放器的接口以提供在创建播放器对象后构建不同元素的能力。

【讨论】:

  • 谢谢。在我之前的实现中,我在其自己的类中使用了Builder,正如您上面建议的那样,而不是嵌套在Player 类中。但是,我当前的解决方案使用Builder 接口,因此我拥有的每个复杂对象都使用自己的嵌套构建器,它实现了Builder 接口。通过这种方式,我可以在每个需要它的类中封装Builder 功能,而不必在每次要创建复杂对象时实例化Builder 对象。不过,我将使用智能指针按照您的想法运行。
  • 是否将 Builder 对象传递给 Player 构造器是一个小问题,这是一个小的设计风格变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-05
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多