2012-02-21 66 views
0

这里单类是一组实现了一种适配器模式的C++类:它结合常量和非const引用数据成员

#include <iostream> 

class Cfoo 
{ 
public: 
    explicit Cfoo(int i):i_(i){} 
    void SetI(int i){ i_ = i; } 
    int GetI()const{ return(i_); } 
private: 
    int i_; 
}; 

class CfooHolderConst 
{ 
public: 
    explicit CfooHolderConst(const Cfoo& foo):foo_(foo){} 
    int GetI()const{ return(foo_.GetI()); } 
private: 
    const Cfoo& foo_; 
}; 

class CfooHolderNonConst 
{ 
public: 
    explicit CfooHolderNonConst(Cfoo& foo):foo_(foo){}; 
    int GetI()const{ return(foo_.GetI()); } 
    void SetI(int i){ foo_.SetI(i); } 
private: 
    Cfoo& foo_; 
}; 

int main( int argc, char* argv[]) 
{ 
    const Cfoo myConstFoo(42); 
    CfooHolderConst myConstFooHolder(myConstFoo); 
    std::cout << myConstFooHolder.GetI() << std::endl; 

    Cfoo myNonConstFoo(1); 
    CfooHolderNonConst myNonConstFooHolder(myNonConstFoo); 
    myNonConstFooHolder.SetI(42); 
    std::cout << myConstFooHolder.GetI() << std::endl; 

    return(0); 
} 

我想CfooHolderNonConst和CFooHolderConst合并成一个类,或做不到这一点,从另一个继承一个。 Cfoo的引用是一个问题,因为在CFooHolderConst中它需要定义为const Cfoo &,而在CfooHolderNonConst中它需要Cfoo &。

这是一个类似问题的迭代符/常量性在这里: How to avoid code duplication implementing const and non-const iterators?

...但我希望,因为这并没有满足STL迭代器的要求,有可能是一个简单的解决方案。

在过去,我通过将const和nonconst指针同时作为类成员,并且从重载的构造函数中设置一个或另一个来解决这类问题。这浪费了空间,似乎笨拙。有没有更优雅的解决方案?

回答

1

是的,这是可以做到:

template< typename T > CHolderReader 
{ 
public: 
    explicit CHolderBase(T& t):t_(t){} 
    int Get()const { return t_.GetI(); } 

protected: 
    ~CHolderReader() {} 

protected: 
    T& t_; 
}; 

template< typename T > CHolderReaderWriter : public CHolderReader<T> 
{ 
public: 
    void Set(int i) 
    { 
     t_.SetI(i); 
    } 
}; 

typedef CHolderReader<const Cfoo> CFooHolderConst; 
typedef CHolderReaderWriter<Cfoo> CFooHolderNonConst;  

其实这是意在那里你包裹在它的const或非const状态的基础数据的获取一个例子。除非模板类型是const,但是不允许您写入,否则Reader将保留一个非const引用,所以您可以在需要写入时将其扩展为与CHolderReaderWriter一样。

+0

我的建议是不让数据成员受到保护,因为它破坏封装,而是使用友谊。 – AndrzejJ 2012-02-21 15:07:00

+0

我不喜欢从基类到派生类的友谊。通常我会使用受保护的get()来获取底层。如果您真的坚持,您可以使用私有继承和使用来将Get方法从基类中公开。 我只是在这里说明如何在这种情况下可以完成。 – CashCow 2012-02-21 18:11:56

+0

这看起来很有前途。我可能会选择你的或AndrzejJ的解决方案。 – 2012-02-21 22:07:32

0

我认为把const和非const接口作为一个单独的类是个好主意。

它们为用户提供不同的接口,并且具有不同的语义。在你的例子中,重复也很少。

1

您可以创建一个模板类,会给你的类的const和非const版本常量功能,然后继承与功能扩展非const版本修改成员:

class Cfoo 
{ 
public: 
    explicit Cfoo(int i):i_(i){} 
    void SetI(int i){ i_ = i; } 
    int GetI()const{ return(i_); } 
private: 
    int i_; 
}; 

class CfooHolderNonConst; 

template<class Foo> 
class CFooHolder 
{ 
    friend class CfooHolderNonConst; 
public: 
    explicit CFooHolder(Foo& foo):foo_(foo){} 
    int GetI()const{ return(foo_.GetI()); } 
private: 
    Foo& foo_; 
}; 

typedef CFooHolder<const Cfoo> CfooHolderConst; 

class CfooHolderNonConst: public CFooHolder<Cfoo> 
{ 
public: 
    explicit CfooHolderNonConst(Cfoo& foo):CFooHolder(foo){}; 
    void SetI(int i){ foo_.SetI(i); } 
}; 
+0

几乎相同的解决方案,我的.. – CashCow 2012-02-21 14:36:47

+0

我们显然张贴在彼此的短的时间间隔 - 没看到你的答案时,我贴我的。 – AndrzejJ 2012-02-21 14:56:52

+0

我们在同一时间用相同的解决方案回答。在现实生活中,我遇到过这种情况。 – CashCow 2012-02-21 18:13:17

0

如果你真的想有相同的类(提供语义还有意义),那么我想你想要的东西,如:

const Cfoo f1(5); 
const CfooHolder h1(f1); 

Cfoo f2(0); 
CfooHolder h2(f2); 

我觉得你的希望会是C++来进行以下decissions: a)如果它是const,则将Cfoo对象视为const;如果它不是非const,则视为非const。线索是Cfoo的定义和CfooHolder的定义。如果Cfooconst,那么CfooHolder必须声明const,否则它应该无法编译。如果Cfoo是非常量,则可以创建CfooHolder的可以是const和非const。 b)在const CfooHolder对象中使用时,方法SetI()应该停止编译。在上面的例子中,h1.SetI(6);不应该编译。

我的答案是,如果a)工作,那么b)也会自动工作。问题是达到a),据我所知,这是不可能的。

为此,该属性应该被设置为const或非const,该类的对象的类型为常量或非常量。虽然这个类的对象可以改变这个“状态”,但是属性保持不变。但是,当此类的对象为const(例如,通过常量引用传递参数时)时,只能使用const方法。所以,C++不会支持这个功能,因为它不会那样工作。

另一种可能性是让属性本身同时为常量和非常量,这是没有意义的。

简短的回答是:它不能完成,会有代码重复。如果你真的想避免这种情况,并且包装是足够复杂的担心,唯一的方法是创建一个普通的持有人,然后在常规持有人周围的常量和非常量包装,避免重复到最小。

class CfooHolder 
{ 
public: 
    explicit CfooHolder(Cfoo& foo):foo_(foo){}; 
    int GetI()const{ return(foo_.GetI()); } 
    virtual void SetI(int i){ foo_.SetI(i); } 
protected: 
    Cfoo& foo_; 
}; 

class CfooHolderNonConst : public CfooHolder { 
public: 
    explicit CfooHolderNonConst(Cfoo& foo):CfooHolder(foo){}; 
}; 

class CfooHolderConst: public CfooHolder 
{ 
public: 
    explicit CfooHolderConst(const Cfoo& foo):CfooHolder(const_cast<Cfoo &>(foo)){} 
    void SetI(int i){ throw std::runtime_error("Don't write to me!"); } 
}; 

这并不完美,但它在规定的条件下工作。方法SetI()会引发运行时错误,但如果CfooHolderConst对象声明为const,那么对SetI()的调用将不会编译。

希望这会有所帮助。

0

是否有一个特定的原因,你为什么不能只为非const(可变)访问使用FooHolder,而为const访问使用const FooHolder

你不能在const对象上调用一个非const限定的方法(如SetI),所以它看起来像是你想要的。不过,显然你需要最初创建的持有者对象来自最初的非const Cfoo

例如:

class Cfoo 
{ 
public: 
    explicit Cfoo(int i) : i_(i) {} 
    void SetI(int i) { i_ = i; } 
    int GetI() const { return(i_); } 
private: 
    int i_; 
}; 

class CfooHolder 
{ 
public: 
    explicit CfooHolder(Cfoo& foo) : foo_(foo) {}; 
    void SetI(int i) { foo_.SetI(i); } 
    int GetI() const { return(foo_.GetI()); } 
private: 
    Cfoo& foo_; 
}; 

void bar(CfooHolder &holder, int i) 
{ 
    holder.SetI(i); // fine 
} 

void bar(CfooHolder const &constholder, int i) 
{ 
    holder.SetI(i); 
    // error: method exists, but I can't call it here 
} 
+0

”显然,你需要从最初的非const Cfoo创建持有者对象,但是。“这是一个问题。我真的需要能够基于const Cfoo实例化类。我并不想在那里有一个const_cast,尽管它会相当安全。 – 2012-02-23 10:09:23

+0

(编译时)类型的系统很难在运行时执行一些你不知道的东西。你可以拥有一个抽象基类,并在工厂函数中实例化两个派生版本中的一个,但我认为这会影响你的_wastes空间,看起来很笨拙。同样,只需将“bool amIConst”放在持有人身边并在任何地方进行检查。 – Useless 2012-02-23 10:15:19

相关问题