0

C++ 新手问题。请确认我做对了。

我有一个全球应用程序类产生它的小孩,我需要让孩子们访问一些应用程序设施。所以我决定通过参考将它们传递给孩子们。

我测试了这个想法,如下所示。它似乎工作正常。我只是想确保我没有做危险的事情。可能有什么我忽略的陷阱吗?

爸爸创造了孩子并给了他们他的车钥匙:

#include <iostream>
using namespace std;

class CCarKeys
{
    public:
        CCarKeys(const string& Name) : _Name(Name) {}
        string _Name;
};

class CChild
{
    public:
        CChild(CCarKeys& CarKeys) : _Name("Child"), _CarKeys(CarKeys) {}
        string _Name;
        CCarKeys& _CarKeys;
        void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
};

class CDad
{
    public:
        CDad() : _Name("Dad"), _HondaCarKeys("Honda keys"), _ChevyCarKeys("Chevy keys") {}
        string _Name;
        CCarKeys _HondaCarKeys;
        CCarKeys _ChevyCarKeys;
        CChild *_Boy;
        CChild *_Girl;
        void MakeBoy() {_Boy= new CChild(_HondaCarKeys);}
        void MakeGirl() {_Girl= new CChild(_ChevyCarKeys);}
};

int main ()
{
    CDad Dad;

    Dad.MakeBoy();
    Dad.MakeGirl();
    Dad._Boy->TestHasKeys();
    Dad._Girl->TestHasKeys();
}
4

4 回答 4

0

对我来说看起来不错(如果他们只需要钥匙的话)。他们可能需要爸爸提供的一些其他服务,这些服务稍后会要求 - 例如:

Wallet += MyDad.GasMoney(REQUEST_MAX_AND_PROMISE_TO_BE_HOME_BY_10PM) ;

但是他们没有提到爸爸,所以他们不能这样做。所以我也会让 CChild 构造函数this引用。

class ICashProvider {
public:
  virtual money Request(IPerson,CashRequestFlags) ;
};

class IChaffeur {
public:
  virtual void Drive(IPerson[]) ;
};

等等

然后CChild构造函数需要采用ICashProviderand IChaffeur,就像CWifeand CGirlfriend(and CBoyfriend,也许)一样。在这一点上,我想你可能会意识到,面对Dad's 的职责,这种粒度级别毫无意义,你只是通过强制调用者在某些方法上发送他们自己的方法来给每个人this并验证请求,所以你没有执行乱伦或换尿布。DadthisDadCWife

于 2009-01-29T15:18:45.190 回答
0

通过引用传递与通过指针传递完全一样,除了语义和如果通过引用传递的话,你不能对指针本身做任何事情。

你的代码没问题。

于 2009-01-29T15:23:28.190 回答
0

在您的特定情况下,车钥匙不太可能永久授予,而是根据需要请求并根据请求授予。所以更多的是

class Dad
{
   /** may return NULL if no keys granted */
   CarKeys *requestKeys(CChild forChild);
}

在主应用程序类和子应用程序的更一般情况下,如何在 main() 中创建应用程序和子应用程序共享的数据对象,并将引用传递给每个人。

于 2009-01-29T15:30:03.823 回答
0

这是可能的,并且在您的代码中它不会造成伤害。但这很危险,因为如果您复制 CDad,则键和指针将被复制。但是,指针将指向的对象以及这些对象中的引用将保持不变。如果原始 CDad 对象超出范围,则指针引用的对象中的引用悬空,不再引用有效对象。

也许您可以反转生命周期:在堆上创建键,并将孩子作为班级中的普通成员。因此,如果您复制爸爸,则复制孩子,但不会复制钥匙。我认为密钥是不可变的,因此您可以在多个孩子之间共享同一个密钥。

这带来了另一点:如果您的密钥相当小(阅读:不是很大)并且不可变(因此,如果您更改一个密钥而不是其他密钥,则不会出现更新异常),请考虑不要在堆上创建它 -所以它们也会被自动复制,并在孩子需要钥匙时传递给他们。您可以让它们成为孩子们的正常指针,但我认为这很丑陋,因为孩子不包含密钥 - 但使用它。所以指针/引用或函数参数很适合,但不是“真实”数据成员。

如果你要使用共享密钥和堆上的密钥,你应该使用智能指针——因为你必须跟踪所有的孩子和爸爸。如果最后一个孩子/爸爸超出范围,您必须再次删除密钥。您boost::shared_ptr为此使用:

class CCarKeys
{
    public:
        CCarKeys(const string& Name) : _Name(Name) {}
        string _Name;
};

class CChild
{
    public:
        CChild(boost::shared_ptr<CCarKeys> const& CarKeys) 
            : _Name("Child"), _CarKeys(CarKeys) {}
        string _Name;
        boost::shared_ptr<CCarKeys> _CarKeys;
        void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
};

class CDad
{
    public:
        // NOTE: null the kid pointers *if* you use raw pointers, so you can check whether
        // a boy or girl is present. Without nulling them explicitly, they have 
        // indeterminate values. Beware. shared_ptr's however will automatically
        // initialized to default "null" values
        CDad() : 
            _Name("Dad"), 
            _HondaCarKeys(new CCarKeys("Honda keys")), 
            _ChevyCarKeys(new CCarKeys("Chevy keys")) {}
        string _Name;

        boost::shared_ptr<CCarKeys> _HondaCarKeys;
        boost::shared_ptr<CCarKeys> _ChevyCarKeys;

        // also use shared_ptr for the kids. try to avoid raw pointers. 
        boost::shared_ptr<CChild> _Boy;
        boost::shared_otr<CChild> _Girl;

        void MakeBoy() {_Boy.reset(new CChild(_HondaCarKeys));}
        void MakeGirl() {_Girl.reset(new CChild(_ChevyCarKeys));}
};

// main can be used unchanged

当然,您可以通过使 CDad 类不可复制来避免所有这些复杂性。然后你可以使用你原来的解决方案,只是让孩子们使用 shared_ptr 并使孩子们也不可复制。理想情况下,应该使用非共享指针,例如auto_ptr,但是 auto_ptr 也有一些陷阱,shared_ptr 都避免了:

class CCarKeys
{
    public:
        CCarKeys(const string& Name) : _Name(Name) {}
        string _Name;
};

class CChild
{
    public:
        CChild (CCarKeys& CarKeys) 
            : _Name("Child"), _CarKeys(CarKeys) {}
        string _Name;
        CCarKeys &_CarKeys;
        void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
    private:
        CChild(CChild const&); // non-copyable
        CChild & operator=(CChild const&); // non-assignable
};

class CDad
{
    public:
        CDad() : 
            _Name("Dad"), 
            _HondaCarKeys("Honda keys"), 
            _ChevyCarKeys("Chevy keys") {}
        string _Name;

        CCarKeys _HondaCarKeys;
        CCarKeys _ChevyCarKeys;

        // also use shared_ptr for the kids. try to avoid raw pointers. 
        boost::shared_ptr<CChild> _Boy;
        boost::shared_otr<CChild> _Girl;

        void MakeBoy() {_Boy.reset(new CChild(_HondaCarKeys));}
        void MakeGirl() {_Girl.reset(new CChild(_ChevyCarKeys));}
private:
    CDad(CDad const&); // non-copyable
    CDad & operator=(CDad const&); // non-assignable
};

如果我必须实现这样的类层次结构,我会采用该解决方案,或者只是将键作为成员删除,并在需要时将它们传递/创建给孩子。关于您的代码的其他一些说明:

  • 最好从成员中删除“_”或将它们放在末尾或使用其他符号。以下划线开头后跟大写字母的名称由 C++ 实现(编译器、C++ 标准库 ...)保留。
  • 我个人觉得让成员名称和变量以大写字母开头令人困惑。我只是很少看到它。但这没什么好在意的,这只是个人风格。
  • 有一条著名的规则(零一无穷)规定,当你得到两件东西时,你通常应该能够拥有任意多件东西。所以如果你可以生两个孩子——为什么不生很多呢?两个似乎是一个任意的选择。但在你的情况下它可能有一个很好的理由 - 所以在你的情况下它是有意义的,请忽略这一点。
于 2009-01-29T15:41:00.323 回答