Luc*_*lle 24 c++ pointers const smart-pointers
对于C++新手来说,允许const成员函数在类引用的对象(通过指针或引用)上调用非const方法通常会让人感到困惑.例如,以下内容完全正确:
class SomeClass
{
class SomeClassImpl;
SomeClassImpl * impl_; // PImpl idiom
public:
void const_method() const;
};
struct SomeClass::SomeClassImpl
{
void non_const_method() { /*modify data*/ }
};
void SomeClass::const_method() const
{
impl_->non_const_method(); //ok because impl_ is const, not *impl_
};
Run Code Online (Sandbox Code Playgroud)
但是,如果constness传播到尖头对象,它有时会非常方便(我自愿使用PImpl习语,因为它是我认为"constness传播"非常有用的情况之一).
使用指针时,可以通过使用某种智能指针轻松实现这一点,操作符在constness上重载:
template < typename T >
class const_propagating_ptr
{
public:
const_propagating_ptr( T * ptr ) : ptr_( ptr ) {}
T & operator*() { return *ptr_; }
T const & operator*() const { return *ptr_; }
T * operator->() { return ptr_; }
T const * operator->() const { return ptr_; }
// assignment operator (?), get() method (?), reset() method (?)
// ...
private:
T * ptr_;
};
Run Code Online (Sandbox Code Playgroud)
现在,我只需要修改SomeClass::impl_是一个const_propagating_ptr<SomeClassImpl>获得想要的行为.
所以我对此有几个问题:
正如 @Alf P. Steinbach 所指出的,您看到了这样一个事实:复制指针会产生一个指向同一底层对象的非常量对象。Pimpl(如下)通过执行深度复制很好地规避了这个问题,unique_ptr通过不可复制来规避它。当然,如果受指点者由单一实体拥有,那就容易得多。
Boost.Optional传播常量性,但它并不完全是一个指针(尽管它模拟了OptionalPointee 概念)。据我所知没有这样的其他图书馆。
我希望他们默认提供它。添加另一个模板参数(我猜是特征类)似乎不值得这么麻烦。然而,这将从根本上改变经典指针的语法,所以我不确定人们是否准备好接受它。
Pimpl班级代码
template <class T>
class Pimpl
{
public:
/**
* Types
*/
typedef T value;
typedef const T const_value;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
/**
* Gang of Four
*/
Pimpl() : _value(new T()) {}
explicit Pimpl(const_reference v) : _value(new T(v)) {}
Pimpl(const Pimpl& rhs) : _value(new T(*(rhs._value))) {}
Pimpl& operator=(const Pimpl& rhs)
{
Pimpl tmp(rhs);
swap(tmp);
return *this;
} // operator=
~Pimpl() { boost::checked_delete(_value); }
void swap(Pimpl& rhs)
{
pointer temp(rhs._value);
rhs._value = _value;
_value = temp;
} // swap
/**
* Data access
*/
pointer get() { return _value; }
const_pointer get() const { return _value; }
reference operator*() { return *_value; }
const_reference operator*() const { return *_value; }
pointer operator->() { return _value; }
const_pointer operator->() const { return _value; }
private:
pointer _value;
}; // class Pimpl<T>
// Swap
template <class T>
void swap(Pimpl<T>& lhs, Pimpl<T>& rhs) { lhs.swap(rhs); }
// Not to be used with pointers or references
template <class T> class Pimpl<T*> {};
template <class T> class Pimpl<T&> {};
Run Code Online (Sandbox Code Playgroud)