2011-07-17 50 views
2

我有一个问题,我不知道如何处理它。首先,我是初学者。我有一个类和两个引用变量声明的两个构造函数,但我不能在一个构造函数中使用两个引用。如果cons1()被调用,我需要ref1被使用,如果cons2被调用,我需要ref2被使用。问题在于我应该如何引用ref1,何时调用cons2,以及类似于调用cons1时ref2应该引用的内容。我不确定如何初始化这些引用。它不能为NULL。我不确定指出一些无效的实现是否是一个好主意。应该是?这甚至是一个选择吗?在C++中如何处理这样的问题?可选的参考实现

// A.cpp 
Class A 
A(Object1& a) : ref1(a) {} - here how should ref2 be handled? 
A(Object2& b) : ref2(b) {}- here what should ref1 reference? 

// A.h 
Object1& ref1 
Object2& ref2 

我需要在这里使用引用。我明白我们可以使用指针而不是引用,但问题是使用引用的特定问题。

+0

你可以改进问题标题吗?我们知道这是一个问题,因为您发布了它,并且我们知道它是C++,因为它是如此标记的。 –

+2

*“我需要在这里使用参考”* - 为什么对自己施加任意限制?如果确实如此,那么我会投票结束,因为太局部化了。如果这是一个真正的编程问题,我们使用正确的工具进行工作,我们不会对自己施加任意限制。 –

+1

虽然问题确实出现在我的个人工作中,但它不仅仅是一个本地化的问题。一般来说,问题是我们如何处理C++中的这种情况。如果使用指针代替refs是唯一的解决方案,那么我们可以关闭它,说出那个soln。 – leonidus

回答

7

由于REF1和REF2是可选的(不是必须的),那么最好是使用指针,而不是引用:

class A 
{ 
    public: 
    A(Object1& a) : ref1(&a), ref2(NULL) {} 
    A(Object2& b) : ref1(NULL),ref2(&b) {} 

Object1 *ref1; 
Object2 *ref2; 
}; 

但后来你必须检查REF1和REF2是NULL。

+0

这个答案是正确的。不过,这似乎是一个糟糕的方法来接收代码。 –

+0

@Tomalak为什么?我猜你可以使用boost :: optional –

+2

因为该对象被赋予了基于不超过构造函数参数类型的非常不同的语义。你必须非常小心,'Object1'和'Object2'之间没有隐含的转换,否则你会得到意想不到的行为,并且在'A'中的每个函数中,你将不得不检查使用哪个和重复代码取决于它是哪个地方。根据更广泛的用例,接口式基类和两个派生类变体可能会更有意义。 –

0

如果你需要有NULL值,你应该从引用切换到指针。一些编译器支持NULL引用,但这是糟糕的风格,不便携,因此应该避免。

+0

所有编译器都支持NULL引用。问题是 - 取消引用NULL引用对象是一个UB。 –

+1

什么编译器?根据定义,一个引用(在C++中)**不能**为NULL –

+0

我相信NULL在stdlib.h中定义.... – Prime

2

警告:以下是愚蠢和做作。为什么?因为使用引用的要求也是如此。

class base { 
    virtual 
    ~base(); 

    virtual 
    void 
    stuff_happens() = 0; 
}; 
base::~base() = default; 

class case1: public base { 
public: 
    explicit 
    case1(Object1& o) 
     : ref(o) 
    {} 

    void 
    stuff_happens() 
    { 
     // we use ref here 
    } 

private: 
    Object1& ref; 
}; 

class case2: public base { 
public: 
    explicit 
    case2(Object2& o) 
     : ref(o) 
    {} 

    void 
    stuff_happens() 
    { 
     // we use ref here 
    } 

private: 
    Object2& ref; 
}; 

std::unique_ptr<base> 
make_base(Object1& o) 
{ return std::unique_ptr<base>(new case1(o)); } 

std::unique_ptr<base> 
make_base(Object2& o) 
{ return std::unique_ptr<base>(new case2(o)); } 

// ... 
{ 
    auto p = condition ? make_base(ref1) : make_base(ref2); 
    p->stuff_happens(); 
} 
1

正确答案已经给出:切换到指针。这种情况下的参考文献提供了零利益,正如你所看到的那样,有一个巨大的缺点。你的设计是可疑的开始;让我们不要因为引入额外的不良要求而使事情复杂化。

但是,如果你坚持使用引用,唯一可能的解决方案是引入某种标记值。

struct A { 
    A(Object1& o) : obj1(o), obj2(null2) { } 
    A(Object2& o) : obj1(null1), obj2(o) { } 

    void function() { 
     if(&obj1 == &null1) 
      //Object2 constructor 
     else 
      //Object1 constructor 
    } 

    private: 
    Object1& obj1; 
    Object2& obj2; 

    static Object1 null1; 
    static Object2 null2; 
}; 

//implementation file 
Object1 A::null1; 
Object2 A::null2; 

此方法取决于Object1Object2两者都具有默认构造函数,或以其它方式具有用于哨兵一个合理的默认值。 null1null2都将在应用程序的整个生命周期中存在,所以如果他们获得任何资源,您将基本上有泄漏。

对于哨兵的无效使用,您也必须小心谨慎,因为[一般情况下]此类访问非常安全。有一个强有力的论据可以说,错误导致可能会崩溃的未定义行为比导致定义良好的行为可能会产生意想不到的结果更好。