C++11:不可拷贝(noncopyable)类,以及boost::
noncopyable介绍
拷贝
拷贝是任何⼀门编程语⾔都必不可少的操作。在 C++ ⾥,拷贝有等号拷贝和构造拷贝之分:
Foo foo, foo2;
Foo foo2 = foo;  // 等号拷贝
Foo foo3(foo);  // 构造拷贝
等号拷贝是显式的,总得有个等号 = 在那才⾏。构造拷贝是隐式的,除了上⾯⽰例代码⾥那种直接写出构造函数的情况,在使⽤以值传递作为参数和以值返回的函数时,都会发⽣构造拷贝:
/// 传⼊ foo 时,发⽣了⼀次实参到形参的构造拷贝
Foo func(Foo foo) {
Foo foo2;
// do somthing
return foo2;
}
/// 返回时,发⽣了⼀次 foo2 到返回值的的构造拷贝
类的等号拷贝和构造拷贝都是可以重载的。如果不重载,默认的拷贝模式是对每个类成员依次执⾏拷贝。
什么时候需要不可拷贝类
考虑⼀种情况,我们要实现⼀个含有动态数组成员的类,其中动态数组成员在构造函数中 new 出来,在析构函数中 delete 掉。⽐如说这样⼀个矩阵类:
template<typename _T>
class Matrix {
public:
int w;
int h;
_T* data;
// 构造函数
Matrix(int _w, int _h): w(_w), h(_h){
data = new  _T[w*h];
}
// 析构函数
~Matrix() {
delete [] data;
}
}
我们在这个类中⽤两个 int 数 w 和 h 代表矩阵的列数和⾏数,⽤⼀个长度为 w*h 的动态数组 data[] 存储矩阵数据。data 在构造函数中分配内存,在析构函数中释放内存。这⾥我们没有重载拷贝函数,那么如果拷贝这个类,会发⽣什么呢?
/// 测试1:等号拷贝
void copy(){
Matrix<double> m1(3,3);
Matrix<double> m2(3,3);
m1 = m2;
}
/// 测试2:传参和返回中的构造拷贝
template<typename _T>
Matrix<_T> copy(Matrix<_T> cpy) {
Matrix<_T> ret(3,3);
return ret;
}
上⾯的测试 1 中,我们先构造了 m1 和 m2 两个 Matrix 实例,这意味着他们各⾃开辟了⼀块动态内存来存储矩阵数据。然后我们使
⽤ = 将 m2 拷贝给 m1,这时候 m1 的每个成员(w,h,data)都被各⾃使⽤ = 运算符拷贝为和 m2 相同的值。m1.data 是个指针,所以就和 m2.data 指向了同⼀块的内存。于是这⾥就会出现两个问题:其⼀, 发⽣拷贝前 m1.data 指向的动态内存区在拷贝后不再拥有指向它的有效指针,⽆法被释放,于是发⽣了内存泄露;其⼆,在 copy() 结束后,m1 和 m2 被销毁,各⾃调⽤析构函数,由于他们的 data 指向同⼀块内存,于是发⽣了双重释放。
测试 2 中也有类似问题。当调⽤ copy(Matrix<_T> cpy) 时,形参 cpy 拷贝⾃实参,⽽ cpy 会在函数结束时销毁,cpy.data 指向的内存被释放,所以实参的矩阵数据也被销毁了——这显然是我们不愿意看见的。同样的,在返回时,ret 随着函数结束⽽销毁,返回值因为拷贝⾃ ret,所以其矩阵数据也被销毁了。
因此,对于像 Matrix 这样的类,我们不希望这种拷贝发⽣。⼀个解决办法是重载拷贝函数,每次拷贝就开辟新的动态内存:
Matrix<_T>& operator = (const Matrix<_T>& cpy) {
w = cpy.w;
h = cpy.h;
delete []  data;
data = new _T[w*h];
memcpy(data, cpy.data, sizeof(_T)*w*h);
return *this;
}
Matrix(const Matrix<_T>& cpy):w(cpy.w), h(cpy.h) {
data = new _T[w*h];
memcpy(data, cpy.data, sizeof(_T)*w*h);
}
这样做也有不好的地⽅。频繁开辟动态内存,当数据量很⼤时(⽐如图像处理),对程序性能是有影响的。在接⼝设计的⾓度考虑,应该把这种拷贝操作以较明显的形式提供给⽤户,⽐如禁⽤等号拷贝,以直接的函数代替 = 操作:
void copyFrom(const Matrix<_T>& cpy) {
w = cpy.w;
h = cpy.h;
delete []  data;
data = new _T[w*h];
memcpy(data, cpy.data, sizeof(_T)*w*h);
}
再禁⽤构造拷贝,只允许⽤户以引⽤传递的办法在⾃定义函数中使⽤ Matrix 类。
那么,如何禁⽌拷贝操作呢?
构造函数可以被重载实现不可拷贝类
使⽤ boost::noncopyable
Boost 作为 C++ 万⾦油⼯具箱,在 <boost/noncopyable.hpp> 下提供了不可拷贝类的实现,使⽤起来也⾮常简单,让⾃⼰的类继承
⾃ boost::noncopyable 即可:
class Matrix : boost::noncopyable
{
// 类实现
}
声明拷贝函数为私有
如果不想⽤第三⽅库,⾃⼰实现呢?不妨先看⼀下 Boost 是怎么做的:
private:  // emphasize the following members are private
noncopyable( const noncopyable& );
noncopyable& operator=( const noncopyable& );
嗯,直接把拷贝函数声明为私有的不就等于禁⽤了么,so smart!于是:
template<typename _T>
class Matrix
{
private:
Matrix(const Matrix<_T>&);
Matrix<_T>& operator = (const Matrix<_T>&);
}
C++ 11 下使⽤ delete 关键字
C++ 11 中为不可拷贝类提供了更简单的实现⽅法,使⽤ delete 关键字即可:
template<typename _T>
class Matrix
{
public:
Matrix(const Matrix<_T>&) = delete;
Matrix<_T>& operator = (const Matrix<_T>&) = delete;
}
其他
关于类似 Matrix 矩阵类的实现,更⾼级的做法是像智能指针⼀样封装其内部数据,⽤内部计数器来确定动态分配的成员是否要释放掉,不过这是另外⼀个问题了。
boost::noncopyable⽐较简单, 主要⽤于单例的情况.
通常情况下, 要写⼀个单例类就要在类的声明把它们的构造函数, 赋值函数, 析构函数, 复制构造函数隐藏到private或者protected之中,每个类都这么做⿇烦.
有noncopyable类, 只要让单例类直接继承noncopyable.
class noncopyable的基本思想是把构造函数和析构函数设置protected权限,这样⼦类可以调⽤,但是外⾯的类不能调⽤,那么当⼦类需要定义构造函数的时候不⾄于通不过编译。但是最关键的是noncopyable把复制构造函数和复制赋值函数做成了private,这就意味着除⾮⼦类定义⾃⼰的copy构造和赋值函数,否则在⼦类没有定义的情况下,外⾯的调⽤者是不能够通过赋值和copy构造等⼿段来产⽣⼀个新的⼦类对象的。
private:  // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
#ifndef BOOST_NONCOPYABLE_HPP_INCLUDED
#define BOOST_NONCOPYABLE_HPP_INCLUDED
namespace boost {
//  Private copy constructor and copy assignment ensure classes derived from //  class noncopyable cannot be copied.
//  Contributed by Dave Abrahams
namespace noncopyable_  // protection from unintended ADL
{
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private:  // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
};
}
typedef noncopyable_::noncopyable noncopyable;
} // namespace boost
#endif  // BOOST_NONCOPYABLE_HPP_INCLUDED
#include "tfun.h"
class myclass: public boost::noncopyable
{
public:
myclass(){};
myclass(int i){};
};
int main()
{
myclass cl1();
myclass cl2(1);
// myclass cl3(cl1);    // error
/
/ myclass cl4(cl2);    // error
return 0;
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。