第十三讲:不同类型数据间的转换

来源:互联网 发布:mac安装windows程序 编辑:程序博客网 时间:2024/06/02 12:51

第十三讲:不同类型数据间的转换

本讲基本要求

    * 掌握:转换构造函数的定义(指定类型的数据与类对象的互相转换)。
    * 理解:标准类型的数据间的转换;类型转换函数的应用。
重点、难点
    * 转换构造函数的定义。

一、 C++标准类型数据间的转换

C++中常规数据类型之间的转换有两种:

1、隐式类型转换:转换是由编译系统自动完成的,用户不需干预。这种转换称为隐式类型转换。

例如
    
int i=6;
    i=7.5+i;
   编译系统对7.5是作为double型数处理的,在求解表达式时,先将6转换成double型,然后与7.5相加,得到和为13.5,在向整型变量i赋值时,将13.5转换为整数13,然后赋给i。

2、显式类型转换:程序人员在程序中指定将一种指定的数据转换成另一指定的类型.

         其形式为: 类型名(数据)

例如:int 89.5,其作用是将89.5转换为整型数89。


问题提出:以前我们接触的是标准类型之间的转换,现在用户自己定义了类,就提出了一个问题:一个自定义类的对象能否转换成标准类型的数据?一个类的对象能否转换成另外一个类的对象?譬如,能否将一个复数类数据转换成整数或双精度数?能否将Date类的对象转换成Time类的对象?

二、 转换构造函数(由数所转换成类的对象)

   转换构造函数(conversion constructor function)的作用是将一个其他类型的数据转换成一个指定的类的对象。

先回顾一下以前学习过的几种构造函数:
    1、默认构造函数。以Complex类为例,函数原型的形式为
        
Complex(); //没有参数
    2、用于初始化的构造函数。函数原型的形式为
        
Complex(double r,double i); //形参表列中一般有两个以上参数
    3、用于复制对象的复制构造函数。函数原型的形式为
        
Complex(Complex&c); //形参是本类对象的引用
    4、现在又要介绍一种新的构造函数——转换构造函数。
(指定类型的数据转换成类对象)转换构造函数只有一个形参。

    对象名=类名(类型 数据){构造函数定义}

   例:Complex(double r){real=r;inage=0};
   其作用是将double型的参数r转换成Complex类的对象,将r作为复数的实部,虚部为0。用户可以根据需要定义转换构造函数,在函数体中告诉编译系统怎样去进行转换。

说明:
   1、在类体中,可以有转换构造函数,也可以没有转换构造函数,视需要而定。
以上几种构造函数可以同时出现在同一个类中,它们是构造函数的重载。编译系统会根据建立对象时给出的实参的个数与类型去选择形参与之匹配的构造函数。
    如:在Complex类中定义了上面的构造函数,在Complex类的作用域中有以下声明语句:
    c1=Complex(3.6); //假设c1已被定义为Complex类对象
   
建立一个无名的Complex类对象,其值为(3.6+0i),然后将此无名对象的值赋给cl,c1在赋值后的值是(3.6+0i)。

    2、如果已对运算符“+”进行了重载,使之能进行两个Complex类对象的相加。
若在程序中有以下表达式:
                    c=c1+2.5;//编译出错

  
因为不能用运算符“+”将一个Complex类对象和一个浮点数相加。可以先将2.5转换为Complex类无名对象,然后相加:
                    c=c1+Complex(2.5); //合法

   3、 转换构造函数的函数体是根据需要由用户确定的,务必使其有实际意义
  例如也可以这样定义转换构造函数:
      Complex(doubler){real=O;imag=r;} //没有人会这样做。应该符合习惯.合乎情理
即实部为0,虚部为r。这并不违反语法。

注意转换构造函数只能有一个参数。如果有多个参数,就不是转换构造函数。原因是显然的:如果有多个参数的话,究竟是把哪个参数转换成Complex类的对象呢?
归纳起来,使用转换构造函数将一个指定的数据转换为类对象的方法如下:

(1)先声明一个类(如上面的Complex)。
(2)在这个类中定义一个只有一个参数的构造函数,参数的类型是需要转换的类型,函数体中指定转换的方法。
(3)在该类的作用域内可以用以下形式进行类型转换:

      类名(指定类型的数据)

就可以将指定类型的数据转换为此类的对象。

例:类型转换可应用场地两个类之间:Teacher与 Student可在Teacher中定义:

Teacher(Student &s){num=s.num;strcpy(name,s.name);sex=s.sex;}//合法

注意(可见性):
对象s中的num,name,sex必须是公用成员,否则不能被类外引用。

三、类型转换函数(类的对象转换为一个其他类型的数据)

   类型转换函数(type conversion function)的作用是将一个类的对象转换成另一类型的数据。

类型转换函数的一般形式为:
   operator 类型名( )
      { 实现转换的语句 }

说明:
   
在函数名前面不能指定函数类型,函数没有参数
。其返回值的类型是由函数名中指定的类型名来确定的。类型转换函数只能作为成员函数,因为转换的主体是本类的对象。不能作为友元函数或普通函数。

例如:
   
已声明了一个Complex类,可以在Complex类中这样定义类型转换函数:
      operator double()
         {return real;}

   请注意:
   1、函数名是operator double。这点是和运算符重载时的规律一致的(在定义运算符“+”的重载函数时,函数名是operator+)。
   2、从函数形式可以看到,它与运算符重载函数相似,都是用关键字operator开头,只是被重载的是类型名。在前面列出的类型转换函数中,double类型经过重载后,除了原有的含义外,还获得新的含义(将一个Complex类对象转换为double类型数据,并指定了转换方法)。这样,编译系统不仅能识别原有的double型数据,而且还会把Complex类对象作为double型数据处理。
   3、定义了前面的类型转换函数后,程序中的Complex类对象是不是一律都转换为double类型数据呢?不是的,它们具有双重身份,既是Complex类对象,又可作为double类型数据。
   
4、转换构造函数和类型转换运算符有一个共同的功能:当需要的时候,编译系统会自动调用这些函数,建立一个无名的临时对象(或临时变量)。例如,若已定义dl,d2为double型变量,cl,c2为Complex类对象,且类中已定义了类型转换函数。

例9 使用类型转换函数的简单例子。
#include <iostream>
using namespace std;
class Complex
   { public:
      Complex(){real=0;imag=0;}
      Complex(double r,double i){real=r;imag=i;}
      operator double() {return real;} //类型转换函数
     private:
      double real;
      double imag; };

int main()
 { Complex c1(3,4),c2(5,-10),c3;
   double d;
   d=2.5+c1; //要求将一个double数据与Complex类数据相加
   
cout<<d<<endl;
   return 0; }

分析:
   
(1)如果在Complex类中没有定义类型转换函数operator double,程序编译将出错。请注意,程序中不必显式地调用类型转换函数,它是自动被调用的,即隐式调用。
   (2)如果在main函数中加一个语句: c3=c2; 由于赋值号两侧都是同一类的数据,是可以合法进行赋值的,没有必要把c2转换为double型数据。
   (3)如果在Complex类中声明了重载运算符“+”函数作为友元函数,并在类外定义operator+函数:
      Complex operator+(Complexcl,Complexc2) //定义运算符“+”重载函数
      {return Complex(cl.real+c2.real,c1.imag+c2.imag);}
若在main函数中有语句:    c3=cl+c2;
   由于已对运算符“+”重载,使之能用于两个ComPlex类对象的相加,因此将cl和c2按Complex类对象处理,相加后赋值给同类对象c3。
如果改为:   d=cl+c2; //d为double型变量
   
例10 包含转换构造函数、运算符重载函数和类型转换函数的程序。

#include <iostream>
using namespace std;
class Complex
   { public:
      Complex(){real=0;imag=0;} //默认构造函数
      
Complex(double r){real=r;imag=0;} //转换构造函数
      
Complex(double r,double i){real=r;imag=i;} //实现初始化的构造函数
      
friend Complex operator + (Complex c1,Complex c2);//重载运算符“+”的友元函数
      
void display();
     private:
      double real;
      double imag; };

Complex operator + (Complex c1,Complex c2) //定义运算符“+”重载函数
{ return Complex(c1.real+c2.real, c1.imag+c2.imag);}

void Complex::display()
{cout<<"("<<real<<"+"<<imag<<"i)"<<endl;}

int main()
 { Complex c1(3,4),c2(5,-10),c3;
   c3=c1+2.5; //复数与double数据相加
   
c3.display();
   return 0; }

对程序的分析:
   
(1)如果没有定义转换构造函数,则此程序编译出错,因为没有重载运算符使之能将Complex类对象与double数据相加。由于c3是Complex类对象,必须设法先将2.5转换为Complex类对象,然后与cl相加,再赋值给d。
   (2)现在,在类Complex中定义了转换构造函数,并具体规定了怎样构成一个复数。由于已重载了运算符“+”,在处理表达式cl+2.5时,编译系统把它解释为 operator+(cl,2.5) 由于2.5不是Complex类对象,系统先调用转换构造函数Complex(2.5),建立一个临时的Complex类对象,其值为(2.5+Oi)。上面的函数调用相当于: operator+(c1,Complex(2.5))。 将cl与(2.5+Oi)相加,赋给c3。运行结果为:(5.5+4i)
   (3)在已定义了相应的转换构造函数情况下,将运算符“+”函数重载为友元函数,在进行两个复数相加时,可以用交换律。 如果运算符“+”重载函数不作为Complex类的友元函数,而作为Complex类的成员函数,能否得到同样的结果呢?请先思考一下。
    (4)如果一定要将运算符函数重载为成员函数,而第一个操作数又不是类对象时,只有一个办法能够解决,再重载一个运算符“+”函数,其第一个参数为double型。当然此函数只能是友元函数,函数原型为
   friend operator+(double,Complex&);
显然这样做不太方便,还是将双目运算符函数重载为友元函数方便些。
   (5)在上面程序的基础上增加类型转换函数:
         operator double(){refurn real;}
      此时Complex类的公用部分为
         public:
         Complex(){real=O;imag=0;}
         Complex(doubler){real=r;imag=O;} //转换构造函数
         Complex(doubler,double i){real=r;imag=i;}
         operator double(){returnreal;} //类刑转换函数
         friend Complex operator+(Complex c1,Complex c2);//重载运算符“+”
         void display();

说明:
   
其余部分变。这时程序在编译时出错,原因是出现二义性。在处理cl+2.5时出现二义性。一种理解是:调用转换构造函数,把2.5变成Complex类对象,然后调用运算符“+”重载函数,与cl进行复数相加。另一种理解是:调用类型转换函数,把cl转换为double型数,然后与2.5进行相加。系统无法判定,这二者是矛盾的。如果要使用类型转换函数,就应当删去运算符“+”重载函数。

 

转自:http://210.44.195.12/cgyy/text/HTML/text/13.htm 

 

 

原创粉丝点击