《C++语言教程》06章 成员函数和运算符的重载


一、构造函数的重载

构造函数可以重载,使得生成实例时非常方便。构造函数一般要对成员变量赋初值,有2种写法:


#include <iostream>
#include <string>
using namespace std;

class stuff {
    string name;
    int age;
public:
    stuff() {        //这是写法一
        cout << name << "---" << age << endl;
        name = "空";
        age = 0;
        cout << name << "---" << age << endl;
    }
    stuff(string n, int a):name(n),age(a)     //这是写法二
    {
        cout << name << "---" << age << endl;
    }
    string getName() {
        return name;
    }
    int getAge() {
        return age;
    }
};

int main ( ) 
{
    stuff st2;
    stuff st1("小雅", 27);

    return 0;
}

写法一是在构造函数体中赋值,赋值前成员变量已经有了地址空间,尚未有值。写法二是一种特殊方法,是在成员变量分配空间的同时将参数的值赋给成员变量。虽然写法二用的人少,但明显优于写法一。

事实上,如果将成员变量的定义改为常量,“const string name;”和“const int age;”,写法一将出错,而写法二仍然正确。

二、运算符重载

运算符重载对于普通函数和成员函数来说,格式稍有不同。

//单目运算符
成员函数:      返回值类型  operator  运算符  () ;
普通函数:      返回值类型  operator  运算符  (对象的类型) ;

//双目运算符
成员函数:      返回值类型  operator  运算符  (对象的类型) ;
普通函数:      返回值类型  operator  运算符  (对象的类型1, 对象的类型2) ;

//函数调用
成员函数:      返回值类型  operator  (任意的参数列) ;

//数组元素
成员函数:      返回值类型  operator[]  (参数类型) ;

//增1/减1运算符
成员函数:      返回值类型  operator  运算符  (int) ;
普通函数:      返回值类型  operator  运算符  (对象的类型, int) ;

#include <iostream>
#include <string>
using namespace std;

class stuff {
    string name;
    int age;
public:
    stuff(string n, int a):name(n),age(a)
    {
        cout << name << "---" << age << endl;
    }
    string getName() {
        return name;
    }
    int getAge() {
        return age;
    }
    void operator +(int x);       //运算符重载的定义
    void operator +(string s);    //运算符重载的定义
};

void stuff::operator +(int x)       //运算符重载的实装
{
    age = age + x;
}
void stuff::operator +(string s)    //运算符重载的实装
{
    name = name + s;
}

int main ( ) 
{
    stuff st2("小雅", 27);
    st2 + 3;          //+运算
    st2 + ".诗经";    //+运算
    cout << st2.getName() << st2.getAge() << endl;

    return 0;
}

三、拷贝构造函数和赋值运算符

本节内容较深,初学者请跳过。“拷贝构造函数”和“赋值运算符”都是将对象的值复制一份然后传给另一对象。这二个功能也是类本身就具有的,但有很多场合原封不动地复制给另外一个对象时反而会出错,例如在成员函数中有动态分配内存,或者参数指针指向外部某一地址时,就有可能出错。

要避免这些错误,我们可以重载“=”运算符以及拷贝构造函数,将出错的因素排除。下例中为了演示,故意将赋值运算符重载函数中不复制“姓名”,而拷贝构造函数中固定“年龄”。


#include <iostream>
#include <string>
using namespace std;

class stuff {
    string name;
    int age;
public:
    stuff(string n, int a):name(n),age(a)
    {
        cout << "构造函数  " << name << age << endl;
    }
    string getName() {
        return name;
    }
    int getAge() {
        return age;
    }
    stuff& operator =(stuff& x);    //赋值运算符重载
    stuff(stuff& x):name(x.name),age(20)    //拷贝构造函数重载
    {
        cout << "拷贝构造函数  " << name << age << endl;
    }
};

stuff& stuff::operator =(stuff& x)
{
    age = x.age;
    cout << "赋值运算符  " << name << age << endl;
    return *this;
}

int main ( ) 
{
    stuff st("小雅", 25);     //调用通常的构造函数
    stuff st1("劝学网", 2);   //调用通常的构造函数
    st1 = st;         //因为不产生新的实例,所以调用的是赋值运算符
    stuff st2 = st;   //因为产生新的实例,所以调用的是拷贝构造函数
    cout << st.getName() << st.getAge() << endl;
    cout << st1.getName() << st1.getAge() << endl;
    cout << st2.getName() << st2.getAge() << endl;

    return 0;
}

四、类型转换

当需要将当前类的实例直接赋值给其它类型的变量时自动转换类型,这其实还是运算符重载。当需要其它类型直接赋值给当前类的实例时,只要增加构造函数就行。


#include <iostream>
#include <string>
using namespace std;

class stuff {
    string name;
    int age;
public:
    stuff(string n, int a):name(n),age(a) { }
    string getName() {
        return name;
    }
    int getAge() {
        return age;
    }
    operator int() {     //stuff → int
        return age;
    }
    operator string() {  //stuff → string
        return name;
    }
};

int main ( ) 
{
    stuff st("小雅", 25);
    string m_name = st; //stuff → string
    int m_age = st;     //stuff → int
    cout << m_name << endl;
    cout << m_age << endl;

    return 0;
}