第一章 C++对C的扩展-2

四.操作符重载

    前面用到的<<本身在C语言中时位操作中的左移运算符。现在又用作流插入运算符,这种一个字符多钟用处的现象叫做重载。在C语言本身就有用重载的现象,比如&即表示取地址,又表示位操作中的与。*即表示解引用,又表示乘法运算符。只不过C语言并没有发放重载机制。

    C++提供了运算符重载机制。可以为自定义数据类型重载运算符。实现构造类型也可以像基本类型一样的运算特性。

#include <iostream>
#include <iomanip>

using namespace std;

struct COMP
{
    float real;
    float image;
};

COMP operator +(COMP t1, COMP t2)
{
    t1.image += t2.image;
    t1.real += t2.real;

    return t1;
}

int main()
{
    COMP c1 = {1, 2};
    COMP c2 = {3, 4};

    COMP sum = c1 + c2; //operate +(c1, c2);
    cout << sum.real << " " << sum.image << endl;

    return 0;
}

     示例中重载了一个全局的操作符+号用于实现两个自定义结构体类型相加。本质是函数的调用。

    当然这个COMP operate +(COMP t1, COMP t2),也可以定义为COMP add(COMP t1, COMP t2),但这样的话,就只能COMP sum = add(c1, c2),而不能实现COMP sum = c1 + c2 了。

   后序我们在学习完成类之后,重点讲解重载。

五.默认参数

    通常情况下,函数在调用时,形参从实参那里取得值。对于多次调用一个函数用同一实参时,C++给出了更简单的处理办法。给形参以默认值,这样就不用从实参那里取值了。

5.1.示例

    单个参数

#include <iostream>
#include <ctime>

using namespace std;

void weatherForcast(char * w="sunny")
{
    time_t t = time(0);
    char tmp[64];
    strftime(tmp, sizeof(tmp), "%Y/%m/%d %X %A ", localtime(&t));
    cout << "today is weather " << w << endl;
}

int main()
{
    //sunny windy cloudy foggy rainy
    weatherForcast();
    weatherForcast("rainny");
    weatherForcast();
    return 0;
}

多个参数

#include <iostream>

using namespace std;

float volume(float length, float weight=4, float high=5)
{
    return length*weight*high;
}

int main()
{
    float v = volume(10);
    float v1 = volume(10, 21);
    float v2 = volume(10, 20);

    cout << v << endl;
    cout << v1 << endl;
    cout << v2 << endl;

    return 0;
}

5.2.规则

  1. 默认的顺序,是从右往左,不能跳跃。
  2. 函数声明和定义一体时,默认参数在定义(声明)处。声明在前,定义在后,默认参数在声明处。
  3. 一个函数,不能既作重载, 又作默认参数的函数。当你少写一个参数时,系统无法确定是重载还是默认参数。 
#include <iostream>

using namespace std;

void print(int a)
{

}

void print(int a, int b=10)
{

}

int main()
{
    print(10);
    return 0;
}
F:\qt-test\const\main.cpp:17: 错误:call of overloaded 'print(int)' is ambiguous

 六.引用

6.1.引用的概念

    变量名,本身是一段内存的引用,即别名(alias)。此处引入引用,是为已有变量起一个别名

    声明如下:

int main()
{
    int a;
    int &b = a;
}

6.2.规则

  •   引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
  • 声明的时候必须初始化,一经声明,不可更改。
  • 可对引用,再次引用。多次引用的结果,是某一变量具有对个别名。
  • &符号前有数据类型时,是引用,其他皆为取地址。
int main()
{
int a,b;
int &r = a;
int &r = b; //错误,不可更改原有的引用关系
float &rr = b; //错误,引用类型不匹配
cout<<&a<<&r<<endl; //变量与引用具有相同的地址。
int &ra = r; //可对引用更次引用,表示a 变量有两个别名,分别是r 和ra
}

6.3.应用

    C++很少使用独立变量的引用,如果使用某一个变量,就直接用它的原名,没有必要使用它的别用。

    值作函数参数(call by value)

void swap(int a, int b); //无法实现两个数据的交换
void swap(int *p, int *q); //开辟了两个指针空间实现交换

    引用作函数参数(call by reference)

void swap(int &a, int &b)
{
    int tmp;
    tmp = a;
    a = b;
    b = tmp;
}

int main()
{
    int a = 3, b = 5;
    cout<<"a = "<<a<<"b = "<<b<<endl;
    swap(a, b);
    cout<<"a = "<<a<<"b = "<<b<<endl;
    return 0;
}

    C++中引入引用后,可以用引用解决的问题,避免用指针来解决。

6.4.引用提高

    引用的本质是指针,C++对裸露的内存地址(指针)作了一次包装。又取得的指针的优良性。所以再对引用取地址,建立引用的指针没有意义。

  •     可以定义指针的引用,但不能定义引用的引用。
int a;
int *P = &a;
int * &rp = p; //ok
int &r = a;
int &&rr = r; //error

    案例:

#include <iostream>

using namespace std;

void swap(char *pa, char *pb)
{
    char *t;
    t = pa;
    pa = pb;
    pb =  t;
}
void swap2(char **pa, char **pb)
{
    char *t;
    t = *pa;
    *pa = *pb;
    *pb = t;
}
void swap3(char * &pa, char * &pb)
{
    char *t;
    t = pa;
    pa = pb;
    pb = t;
}
int main()
{
    char *pa = "china";
    char *pb = "america";

    cout<<"pa :"<<pa<<endl;
    cout<<"pb :"<<pb<<endl;

    //swap(pa, pb);
    //swap2(&pa, &pb);
    swap3(pa, pb);
    cout<<"pa :"<<pa<<endl;
    cout<<"pb :"<<pb<<endl;

    return 0;
}
  •     可以定义指针的指针(二级指针),但不能定义引用的指针。
int a;
int *p = &a;
int **PP = &p; //ok
int &r = a;
int &*pr = &r; //error 
  •     可以定义指针数组,但不能定义引用数组,数组引用。
int a, b, c;
int* parr[] = {&a, &b, &c}; //ok
int& rarr[] = {a, b, c}; //error
int arr[] = {1, 2, 3};
int (&rarr)[3] = arr; //ok
  • 常引用

    const引用有比较多使用。它可以防止对象的值被随意的修改。因而具有一些特性。

        (1)const对象的引用必须是const的,将普通引用绑定到const对象是不合法的。这个原因比较简单。既然对象是const的,表示不能被修改,引用当然也不能修改,必须使用const引用。实际上,const int a=1;int &b = a;这种写法是不合法的,编译不过。

        (2)const引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是const引用与普通引用最大的区别。const int &a = 2;是合法的。double x=3.14;const int &b =x;也是合法的。

        const引用的目的是,禁止通过修改引用值来改变被引用的对象。const引用的初始化特性较为微妙,可通过如下代码说明:

double val = 3.14;
const int &ref = val; //int const & / int & const ??
double &ref2 = val;
cout<<ref<<" "<<ref2<<endl;
val = 4.14;
cout<<ref<<" "<<ref2<<endl;

        上述输出结果为3 3.14和3 4.14。因为ref是const的,在初始化的过程中已经给定值,不允许修改,而被引用的对象是val,是非const的,所以val的修改并不影响ref的值,而ref2的值发生了相应的改变。

        那么ref不是const的,那么改变的是temp,而不是val。期望对ref的赋值会修改val的程序员会发现实际并未修改。(赋值的过程实际是产生了中间变量)

int i = 5;
const int &ref = i+5;
//产生了与表达式等值的无名临时变量,
//此时的引用是对无名的临时变量的引用。故不能修改。
cout<<ref<<endl;
  •     尽可能使用const

        use const whatever possible 原因如下:

  1. 使用const可以避免无意修改数据的编程错误。
  2. 使用const可以处理const和非const实参。否则将只能接受非const数据。
  3. 使用const引用,可使函数能够正确的生成并使用临时变量(如实参与引用参数不匹配,就会生成临时变量)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值