通过引用让孩子访问父母的成员 - 可以吗?

时间:2009-01-29 15:12:41

标签: c++ class class-design pass-by-reference

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 个答案:

答案 0 :(得分: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构造函数需要ICashProviderIChaffeurCWifeCGirlfriend(和CBoyfriend也许) 。在这一点上,我认为您可能会意识到,面对Dad的职责,这种级别的粒度是毫无意义的,您只需通过强制呼叫者为每个人thisDad验证请求在某些方法上发送自己的this,因此您没有Dad执行乱伦或更改CWife的尿布。

答案 1 :(得分:0)

通过引用传递与传递指针完全相同,除了语义之外,如果通过引用传递,则无法对指针本身执行任何操作。

您的代码没问题。

答案 2 :(得分:0)

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

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

在主应用类和子代的更通用的情况下,如何创建由main()中的app和children共享的数据对象,并将其传递给每个人。

答案 3 :(得分: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 ++ std lib ...)保留以下划线开头并后跟大写字母的名称。
  • 我个人觉得让成员名和变量以大写字母开头会让人感到困惑。我很少见到它。但这并不是很值得关注,只是个人风格。
  • 有一条着名的规则(Zero-One-Infinity)说明当你得到两件东西时,你通常应该能够拥有任意多种东西。所以,如果你有两个孩子 - 为什么不能有很多孩子呢?两个似乎是一个随意的选择。但是在你的情况下它可能有一个很好的理由 - 所以在你的情况下忽略它是有意义的。