`
evasiu
  • 浏览: 165805 次
  • 性别: Icon_minigender_2
  • 来自: 广州
博客专栏
Fa47b089-e026-399c-b770-017349f619d5
TCP/IP详解卷一>阅读...
浏览量:12277
社区版块
存档分类
最新评论

c++ premier -- 复制控制与重载操作符

 
阅读更多

第十三、十四章主要讲了复制控制与操作符重载的相关内容。同时也提供了一些例子说明在某些情况下必须使用以及如何使用自定义的复制控制。

复制构造函数是一种特殊的构造函数,它接受该类类型的引用作为其形参。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数,当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数。

析构函数是构造函数的互补,当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。

赋值操作符是在左对象已存在的情况下,使用右操作数对左对象进行赋值。

复制构造函数、析构函数和赋值操作符总称为复制控制。

 

复制构造函数和赋值操作符不一样,复制构造函数是在对象不存在的时候,利用形参创建一个对象;赋值操作符使用的场景是存在一个左对象,对左对象的成员用右操作数进行重新赋值。

#include <iostream>
#include "Query.h"
using namespace std;

int main(){
	Query t( "text" );
//此时q不存在,即使使用的是=,调用的也是复制构造函数
	Query q = t;
	Query p( "string" );
//q已经是一个Query对象了,此时才真正调用赋值操作符
	q = p;
}

另外,赋值构造函数常常需要先确定左右操作数不是同一个对象(&rhs != this )

如果类不允许复制,可以将复制构造函数声明为private的,然而,这样做的时候类的友元和成员仍可以进行复制,如果想要连友元和成员中的复制也禁止,可以声明一个private复制构造函数但不对其定义。不允许复制的类对象只能作为引用传递给函数或者从函数返回,它们也不可以用作容器的元素。

 

重载操作符其实也是一种函数,它可以是类的成员函数,也可以是非成员函数。当作为成员函数时,第一个操作数是this。作为非成员函数时,由于某些操作符会用到类的私有成员(如operator<<),因此可以将其声明为友元。重载操作符必须具有一个类类型操作数。

选择做为成员函数还是非成员函数,有一些指导原则:

1. 赋值(=)、下标([])、调用(())和成员访问(->)必须定义为成员,将这些操作符定义为非成员函数将在编译时标记为错误。

2. 像赋值一样,复合赋值操作符通常应定义为类的成员。

3. 改变对象或与给定类型紧密联系的其他一些操作符,如自增、自减和引引用,通常应定义为成员。

4. 对类的操作符,如算术操作符、相等操作符、关系操作符和位操作符,最好定义为非成员函数。(IO操作符必须为非成员函数)

 

定义下标操作符比较复杂的地方在于,它在用作赋值的左右操作数时都应该能表现正常。下标操作符出现在左边,必须生成左值,可以指定引用作为返回类型而得到左值。只要下标操作符返回引用,就可用作赋值的任意一方。

可以对const和非const对象使用下标。应用于const对象时,返回值应为const,因此不能用作赋值的目标。

定义下标操作符时,一般要定义两个版本,一个为非const成员并返回引用,一个为const成员并返回const引用。

class Foo{
	public:
		int& operator[]( const size_t );
		const int& operator[]( const size_t) const;
	private:
		vector<int> data;
	};

int& Foo::operator[]( const size_t index ){
	return data[index];
}

const int& Foo::operator[]( const size_t index ) const{
	return data[index];
}

解引用操作符跟下标操作符一样,必须定义两个版本,一个为const的,一个为非const的。

箭头操作符比较特殊,它表现起来是一个二元操作符,->后面指向一个成员标识符,我们不大可能将成员标识符作为函数的形参进行调用,因此,在解析箭头操作符时,一般分三步。举个例子,point->action()(实际上等价于(point->action)())

(1)如果point是一个指针,指向具有名为action的成员对象的类对象,则编译器将代码编译为调用该对象的action成员。

(2)否则,如果action是定义了operator->操作符的类的一个对象的成员,则point->action与point.operator->()->action相同,即执行point的operator->(),然后使用该结果重复这三步。

(3)否则,代码出错。

 

自增与自减引用主要必须考虑前缀形式和后缀形式。

 

这两章中对我来说最重要,也是最陌生的概念,即为函数对象(functional object)。函数对象,就是在某种方式上表现得像函数的对象,典型地,它是指一个类的实例,这个类定义了调用操作符operator()。与函数对象相关的概念是函数适配器,就是之前在STL一章留下的空白。不过我感觉我还没有完全学透函数对象的用法,它在标准库中的使用我也没有完全弄明白,有些函数接口也根本看不出来是要用一个函数还是一个函数对象,又好像看到网上有人说到可以通过模板使得它即能接受函数指针,又能接受函数对象,也许看完第16章的模板,我会有一个更好的理解吧,到时候再专门讲一讲函数对象。

 

转换操作符是一种特殊的类成员函数,它定义将类类型值转变了其他类型值的转换。如下:

class smallInt{
	public:
		smallInt( int i=0 ):val(i){
			if( i<0 || i>255 )
				throw std::out_of_range( "Bad SmallInt initializer" );
				}
		//转换操作符,从smallInt到int类型的转换
		operator int() const{ return val; }
	private:
		std::size_t val;
		};

 

基本上就是这样了,这两章讲述了复制控制的三个函数,还有一些重载操作符应该注意的事情,包括应该定义为成员还是非成员函数、几种操作符的重载,下标操作符、解引用操作符、箭头操作符、自增自减、调用操作符(与函数对象相关)、转换操作符,最后还提到了转换与重载的关系,以及可能出现的二义性。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics