一、赋值运算符和拷贝构造函数(重载技术)
赋值运算符和拷贝构造函数有编译器默认提供,但如果想做更复杂的事,需要重载。
1.下面用一个简单的例子先区分一下赋值运算符和拷贝构造函数:
#include<iostream> using namespace std;
class alpha
{ public :
alpha():data(0) {} //没有参数的构造函数
alpha( int d):data(d) {} //一个参数的构造函数
void diplay() //显示数据
{
cout<<data<<endl;
}
alpha(alpha& a) //重载拷贝构造函数
{
data=a.data;
cout<< "copy constructor invoked! " <<endl;
}
alpha operator = (alpha& a) //重载赋值运算符
{
data=a.data;
cout<< "assignment operator invoked! " <<endl;
return alpha(data); //单参数的构造函数
}
private :
int data;
}; int main()
{ alpha a1(32);
alpha a2; //无参数构造函数
a2=a1; //赋值运算符
a2.diplay();
alpha a3=a1; //拷贝构造函数
a3.diplay();
alpha a4(a1); //拷贝构造函数
a4.diplay();
return 0;
} |
1)首先区别什么是赋值,什么是初始化。
2)程序中重载采用了引用传递,原因:①众所周知,用值传递的参数将在要传递的函数内产生一个副本,没有例外。如果这个对象很大,则副本就会浪费汗多空间。②在某些情况下可能想记录对象的数目。如果编译器在使用赋值运算符时,每次产生一个额外的对象,这样就可能见到比医疗多得多的对象。而引用传递有助于避免创建过多的对象。
3)return alpha(data); 返回值是重载函数所在的对象的一个副本,而不是同一个对象。返回的值可使它将运算符=串联起来:a3=a2=a1;
4) 然而alpha operator = (alpha& a)中值的返回与值的参数一样的缺点:浪费内存空间。但是使用引用来返回函数的值如alpha& operator = (alpha& a) 这样行吗?
回答是不行的:在函数内部创建的非static的局部变量,在函数返回时即被销毁,引用所返回的值,仅仅是实际希望返回值得地址,是没有意义的,甚至会出现严重错误。(在这里我们可以用this指针解决这个问题,后文会解释)
注意:赋值运算符是唯一一个不能继承的运算符。如果在基类重载了赋值运算符,那么在任何派生类中都不能再重载同一函数。
2.看函数的几种情况
1)函数参数
void func(alpha); //以值传递对象
func(a1); //函数调用
|
这时拷贝构造函数将会被调用来创建一个对象a1的副本,并将副本交给函数func()操作。(当然引用或者指针就不会调用拷贝构造函数)
2)函数返回值
alpha func() //函数声明
a2=func() //函数调用
|
在这里:首先,程序调用拷贝构造函数来穿件一个func()返回值的副本;
然后,这个值再被赋给a2(调用赋值运算符)。
3)拷贝构造函数为alpha(alpha& a) ,为什么不是alpha(alpha a)的形式?
答:拷贝构造函数必须使用引用,否则会报告内存溢出。(因为如果参数用值来传递,就需要创建一个该值的副本。如何创建副本呢?使用拷贝构造函数,但是原函数本来就是要定义拷贝构造函数,这样不断调用自己你说会有什么结果!~)
二、this指针
每一个对象的成员函数都可以访问一种神奇的指针,即指向该对象本身的this指针,因而在任何对象中都可找到所属对象的自身地址。
#include<iostream> using namespace std;
class alpha
{ public :
alpha():data(0) {}
alpha( int d):data(d) {}
void display()
{
cout<<data<<endl
<< this ->data<<endl;
}
//alpha operator = (alpha& a) //重载赋值运算符
//{
// data=a.data;
// cout<<"assignment operator invoked! "<<endl;
// return alpha(data); //单参数的构造函数
//}
alpha& operator = (alpha& a) //重载赋值运算符
{
data=a.data;
cout<< "assignment operator invoked! " <<endl;
return * this ; //通过this指针返回值
}
private :
int data;
}; int main()
{ alpha a1(37);
a1.display(); //直接输出和this->data的输出结果是一样的
alpha a2,a3;
a3=a2=a1; //调用赋值运算符
cout<< "a2=" ;a2.display();
cout<< "a3=" ;a3.display();
return 0;
} |
注意实例中的:使用this指针返回值。(从成员函数和重载运算符返回值,this指针是一个更实用的用法)
1)this指针指向的是该成员函数所属的对象,所以*this就是这个对象本身。通常实用引用和this指针从重载赋值运算符返回数据,从而避免创建额外的对象。
2)必须注意:this指针在静态成员函数中是无效的,因为静态成员函数不属于任何特定的对象。
三、dynamic_cast和typeid
这两个功能通常使用在有很多类都由一个基类派生的情况下。为了使动态类型转换能够工作,基类必须是多态的(也就是说至少包含一个虚函数)。
1.dynamic_cast可以改变指针类型
#include<iostream> #include<typeinfo> using namespace std;
class Base
{ public :
Base() {}
virtual void vertfunc() //要想用dynamic_cast,基类必须是多态类型
{}
Base( int b):ba(b){}
void show()
{
cout<< "Base: ba=" <<ba<<endl;
}
protected :
int ba;
}; class derv1: public Base
{}; class derv2: public Base
{ public :
derv2():Base() {}
derv2( int b, int d):Base(b),da(d) {}
void show()
{
cout<< "derv2: ba=" <<ba<< " da=" <<da<<endl;
}
private :
int da;
}; bool isDerv1(Base* pUnknown)
{ derv1* pderv1;
if (pderv1= dynamic_cast <derv1*>(pUnknown))
return true ;
else
return false ;
} int main()
{ derv1* d1= new derv1;
derv2* d2= new derv2;
if (isDerv1(d1))
cout<< "d1是类derv1的一个对象" <<endl;
else
cout<< "d1不是类derv1的一个对象" <<endl;
if (isDerv1(d2))
cout<< "d2是类derv1的一个对象" <<endl;
else
cout<< "d2不是类derv1的一个对象" <<endl;
Base* pbase= new Base(10);
derv2* pderv= new derv2(22,33);
pbase->show();pbase= dynamic_cast <Base*>(pderv); pbase->show(); //派生类到基类
pbase= new derv2(34,32);
pderv->show(); pderv= dynamic_cast <derv2*>(pbase); pderv->show(); //基类到派生类
return 0;
} |
2.typeid可以得到未知的对象类型信息
如上面的实例中最后加上:
cout<< typeid (*pbase).name()<<endl;
|
运行程序会显示class derv2,因为pbase=new derv2(34,32); 这条语句。