条款4~5:GotW#16 具有最大可复用的通用Containers

问题:

为下面的定长(fixed-length)vector class实现拷贝构造和拷贝赋值操作,以提供最大的可用性(usability)。提示:请考虑用户代码可能会用它做哪些事情。

template<typename T,size_t size>
class fixed_vector
{
public:
	typedef T* iterator;
	typedef const T* const_iterator;
	iterator begin(){return v_;}
	iterator end(){return v_+size;}
	const_iterator begin() const {return v_;}
	const_iterator end() const {return v_+size;}
private:
	T v_[size];
};

注意:不要修改其他东西,这个容器并不打算遵守STL——它至少有个严重的问题。

解答:

下面的解决方案具体是怎样运作的?为什么这样运作?请对每一个构造函数(constructor)和运算符(operator)都做说明:

template<typename T,size_t size>
class fixed_vector
{
public:	
	typedef T* iterator;
	typedef const T* const_iterator;
	fixed_vector(){}
	
	template<typename O,size_t osize>
	fixed_vector(const fixed_vector<O,osize>& other)
	{
		copy(other.begin(),other.begin() + min(size,osize),
			 begin());
	}
	
	template<typename O,size_t osize>
	fixed_vector<T,size>&
	operator = (const fixed_vector<O,osize>& other)
	{
		copy(other.begin(),other.end() + min(size,osize),
			begin());
		return *this;
	}
	
	iterator begin(){return v_;}
	iterator end(){return v_+size;}
	const_iterator begin() const {return v_;}
	const_iterator end() const {return v_+size;}
private:
	T v_[size];	
};

现在分析这段代码:

【拷贝构造(copy constructor)和拷贝赋值(copy assignment)操作】

原始代码中给出了本身已经工作良好的的拷贝构造和拷贝赋值。而我们的解决方案中欲增加一个模板化的的构造函数和一个模板化的赋值运算符函数,以使得构造操作和赋值操作更具适应性。

但是这两个函数压根不是拷贝构造和拷贝赋值函数。

其原因:真正的拷贝构造和拷贝赋值只对完全相同的类型对象施以构造和赋值操作,并且如果是一个模板类的话,模板的参数也都必须完全相同。例如:

struct X
{
	template<typename T>
	X(const T&);	//不是拷贝构造,T不可能是X
	
	template<typename T>
	operator=(const T&);//不是拷贝赋值,T不可能是X
};

虽然这两个模板化了的成员函数的确具有拷贝构造和拷贝赋值操作的准确形式,其实不然——根本不是这回事,因为在那两种情况下,T都不一定是X。

由于模板构造函数终究不是拷贝构造函数,因此这种模板的出现并不会隐藏原来的拷贝构造函数。

如此一来我们所做的改动只是增强了构造操作和赋值操作的可适应性,而不是替换掉隐藏的版本。举例:

fixed_vector<char,4> v;
fixed_vector<int,4> w;
fixed_vector<int,4> w2(w); //调用隐藏的拷贝构造

fixed_vector<int,4> w3(v); //调用模板化的转换构造函数
w = w2;					   //调用隐藏的拷贝赋值
w = v2;					   //调用模板化的转换赋值函数

由此看出,本条款的问题所寻求的真正答案是提供了可适应性的“从其他fixed_vectors进行构造和拷贝的做操”,而不是具有可适应性的“拷贝构造和拷贝赋值操作”——它们早就存在了。

【构造操作和赋值操作的可用性】

我们增加的两个操作具有如下两个主要用途:

  • 支持可变的类型(包括继承在内)

尽管fixed_vector原则上应该保持在相同类型的container之间的拷贝和赋值操作,但有时候从另一个包含不同类型的对象至fixed_vector进行拷贝和赋值操作,也是不无意义的。只要源对象可以被赋值给目的对象,就应该允许这种不同类型对象之间的赋值。例如,用户代码可能会这样使用fixed_vector:

fixed_vector<char,4> v;
fixed_vector<int,4> w; //拷贝
w = v;				   //赋值

class B{/*...*/};
class D : public B{/*...*/};
fixed_vector<D,4> x;
fixed_vector<B,4> y(x); //拷贝
y = x;					//赋值
  • 支持可变的大小

与第一点类似,用户代码有时候也希望从具体不同大小的fixed_vector进行构造和赋值。支持这样的操作也是有意义的。例如:

fixed_vector<char,6> v;
fixed_vector<int,4> w(v); //拷贝四个对象
w = v;					  //对四个对象进行赋值

class B{/*...*/};
class D : public B{/*...*/};
fixed_vector<D,16> x;
fixed_vector<B,42> y(x); //拷贝16个对象
y = x;					 //对16个对象进行赋值

【另一种解决方案:标准库风格的解答】

虽然上面函数形式及其更加的可能性,但它们还是有些做不到的事情。下面考察具有标准风格的解答:

  • 拷贝(Copying)
template<class RAIter>
fixed_vector(RAIter first,RAIter last)
{
	copy(first,first+min(size,(size_t)last-first),begin);
}

//拷贝就不写成如下的形式
fixed_vector<char,6> v;
fixed_vector<int,4> w(v);

//而是应该写成如下的形式
fixed_vector<char,6> v;
fixed_vector<int,4> w(v.begin(),v.end());

对于一个构造操作而言,哪一种方案更好呢:是先前的预想方案,还是这种标准库风格的方案呢?前一种相对容易使用,而后一种更具可适应性(比如它允许用户选择操作范围并可以其他种类的container进行拷贝)——可以任意选择一种和都提供。

  • 赋值(Assignment)

注意的是,由于operator=()只提供了一个参数,因此我们无法让赋值操作把iterator的范围作为另一个参数。一个可行的方法是,提供一个具名函数(name function):

template<class Iter>
fixed_vector<T,size_t>&
assign(Iter first,Iter last)
{
	copy(first,last+min(size,last-first),begin());
	return *this;
}

//不用这种赋值行销
w = v;
//赋值操作正确形式
w.assign(v.begin(),v.end());

从技术上而言,assign()其实并不需要,没有它我们一样可以达到相同的可适应性。但这样一样,进行的赋值操作就不太有效率:

w = fixed_vector<int,4>(v.begin(),v.end());

对于赋值操作而言,用于很容易编写拷贝操作的代码而不用写assign()函数:

//不用写这种形式
w.assign(v.begin(),v.end());
//可以用这种形式
copy(v.begin(),v.end(),w.begin());

这种情况下就没有必要编写assign()了,使用先前的预想方案更加好点,这样可以让用户代码需要堆某个范围内的对象进行赋值操作的时候直接使用copy()。

【为什么给出了缺省构造函数(default constructor)】

这是因为你一旦定义了任何形式的构造函数,编译器就不会为你生成缺省版本了。显然,像上述代码就需要这样做。

【总结:成员函数模板(Member Function Templates)到底怎样】

希望本期的GotW条款已经使你确信,成员函数模板非常易用。这就是为什么成员函数模板会被广泛用于标准库的原因。

在你创建自己的classes时使用成员模板,不仅可以取悦用户,而且用户还会越来越多,并争先恐后的使用那些极易复用的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值