运算符重载


运算符重载的基本方法:1.使用成员函数重载运算符 2.使用非成员函数(友元函数)重载运算符

一 使用成员函数重载运算符

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

/*
1斤牛肉 = 2斤猪肉
*/

// 猪类
class Pork
{
public:
    Pork(int weight = 0)
    {
        this->weight = weight;
    }

    int getWeight() const { return weight; }

    string description()
    {
        stringstream ret;
        ret << "一头" << weight << "斤的猪";
        return ret.str();
    }
private:
    int weight;
};

// 牛类
class Cow
{
public:
    Cow(int weight = 0)
    {
        this->weight = weight;
    }

    // 使用成员函数重载运算符
    Pork operator+(const Cow& cow);
    Pork operator+(const Pork& pork);
private:
    int weight;
};

// 使用成员函数重载运算符
Pork Cow::operator+(const Cow& cow)
{
    int temp = (this->weight + cow.weight) * 2;
    return Pork(temp);
}

// 使用成员函数重载运算符
Pork Cow::operator+(const Pork& pork)
{
    int temp = this->weight * 2 + pork.getWeight();
    return Pork(temp);
}

int main()
{
    Cow c1(100);
    Cow c2(200);
    Pork p1 = c1 + c2;   // (100+200)*2 = 600
    cout << p1.description() << endl;
    p1 = c1 + p1;        // 100*2 + 600 = 800
    cout << p1.description() << endl;
    return 0;
}

image

二 使用非成员函数(友元函数)重载运算符

//-------------- Pork 类 -------------------
/* Pork.h */
#pragma once
#include <string>
using namespace std;
// 猪类
class Pork
{
public:
    Pork(int weight = 0);

    int getWeight() const;
    string description();
private:
    int weight;
};

/* Pork.cpp */
#include <sstream>
#include "Pork.h"

Pork::Pork(int weight)
{
    this->weight = weight;
}

int Pork::getWeight() const 
{
    return weight; 
}

string Pork::description()
{
    stringstream ret;
    ret << "一头" << weight << "斤的猪";
    return ret.str();
}

//-------------  Cow 类 ----------------
#pragma once
#include "Pork.h"
// 牛类
class Cow
{
public:
    Cow(int weight = 0)
    {
        this->weight = weight;
    }

    // 使用非成员函数(友元函数)重载运算符
    friend Pork operator+(const Cow& cow1, const Cow& cow2);
    friend Pork operator+(const Cow& cow, const Pork& pork);
private:
    int weight;
};

//--------------  main.cpp  -----------------
#include <iostream>
#include <string>
#include <sstream>
#include "Pork.h"
#include "Cow.h"
using namespace std;

/*
1斤牛肉 = 2斤猪肉
*/

// 使用非成员函数(友元函数)重载运算符
Pork operator+(const Cow& cow1, const Cow& cow2)
{
    int temp = (cow1.weight + cow2.weight) * 2;
    return Pork(temp);
}

// 使用非成员函数(友元函数)重载运算符
Pork operator+(const Cow& cow, const Pork& pork)
{
    int temp = cow.weight * 2 + pork.getWeight();
    return Pork(temp);
}

int main()
{
    Cow c1(100);
    Cow c2(200);
    Pork p1 = c1 + c2;   // (100+200)*2 = 600
    cout << p1.description() << endl;
    p1 = c1 + p1;        // 100*2 + 600 = 800
    cout << p1.description() << endl;
    return 0;
}

image

三 使用成员函数和非成员函数两种方法实现运算符重载的区别

  • 区别:使用成员函数来实现运算符重载时,少写一个参数,因为第一个参数就是this指针

  • 两种方式的选择:

    • 1 一般情况下,单目运算符重载,使用成员函数进行重载更方便(不用写参数)
    • 2 一般情况下,双目运算符重载,使用友元函数实现更直观(方便实现 a+b 和 b+a 相同的效果,成员函数方式无法实现。)
      例如: 100 + cow; 只能通过友元函数来实现 cow +100; 友元函数和成员函数都可以实现
  • 特殊情况:

    • 1 = () [ ] -> 不能重载为类的友元函数!!!(否则可能和 C++的其他规则矛盾), 只能使用成员函数形式进行重载。
    • 2 如果运算符的第一个操作数要求使用隐式类型转换,则必须为友元函数(成员函数方式的第一个参数是 this 指针)
  • 同一个运算符重载, 不能同时使用两种方式来重载,会导致编译器不知道选择哪一个(二义性)

四 运算符重载的禁区和规则

1.为了防止对标准类型进行运算符重载,C++规定重载运算符的操作对象至少有一个不是标准类型,而是用户自定义的类型,比如不能重载 1+2 但是可以重载 cow + 2 和 2 + cow // cow 是自定义的对象
2.不能改变原运算符的语法规则, 比如不能把双目运算符重载为单目运算
3.不能改变原运算符的优先级
4.不能创建新的运算符,比如 operator**就是非法的, operator*是可以的
5.不能对以下这四种运算符,使用友元函数进行重载 = 赋值运算符,()函数调用运算符,[ ]下标运算符,->通过指针访问类成员
6.不能对禁止重载的运算符进行重载

不能被重载的运算符:
image

可以被重载的运算符:
image

五 重载运算符+

class Man
{

public:
    Man(string name = "未知", int age = 0, int score = 0);
    Man operator+(const Man& other);
    void print() const;
private:
    string name;
    int age;
    int score;
};

Man::Man(string name, int age, int score)
{
    this->name = name;
    this->age = age;
    this->score = score;
}

Man Man::operator+(const Man& other)
{
    string name = this->name + other.name;
    int age = this->age + other.age;
    int score = this->score + other.score;
    return Man(name, age, score);
}

void Man::print() const
{
    cout << "name:" << this->name << " age:" << this->age << " score:" << this->score << endl;
}

int main()
{
    Man t1("宫本", 13, 30);
    Man t2("武藏", 15, 60);

    cout << "t1: ";
    t1.print();
    cout << "t2: ";
    t2.print();
    Man t3 = t1 + t2;
    cout << "t3: ";
    t3.print();

    return 0;
}

image

六 重载运算符= (注意返回类型和参数类型)

注意:
注意赋值运算符重载的返回类型和参数类型。
返回引用类型,便于连续赋值
参数使用引用类型,可以省去一次拷贝
参数使用 const, 便于保护实参不被破坏。

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    ~Boy();

    Boy& operator=(const Boy& boy);  // 注意返回值类型和参数类型
    string description();
private:
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

// 注意返回值类型和参数类型
Boy& Boy::operator=(const Boy& boy)
{
    if (name)
    {
        delete name; // 释放原来的内存
    }
    this->name = new char[strlen(boy.name) + 1]; // 分配新的内存
    strcpy_s(this->name, strlen(boy.name) + 1, boy.name);
    this->age = boy.age;
    this->salary = boy.salary;
    //this->id = boy.id;  // 根据需求决定要不要拷贝id
    return *this;  // 返回引用可以解决连续赋值的问题 例如 boy1 = boy2 = boy3;
}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int main()
{
    Boy boy1("小明", 18, 3000);
    Boy boy2, boy3;
    cout << boy1.description() << endl;
    cout << boy2.description() << endl;
    cout << boy3.description() << endl;

    cout << endl << "boy3 = boy2 = boy1 执行后" << endl;
    boy3 = boy2 = boy1;
    cout << boy1.description() << endl;
    cout << boy2.description() << endl;
    cout << boy3.description() << endl;

    return 0;
}

image

七 重载比较运算符 > < ==

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    ~Boy();

    Boy& operator=(const Boy& boy);  // 注意返回值类型和参数类型
    bool operator<(const Boy& boy);
    bool operator>(const Boy& boy);
    bool operator==(const Boy& boy);

    string description();
private:
    int power() const;    // 综合能力值
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

// 注意返回值类型和参数类型
Boy& Boy::operator=(const Boy& boy)
{
    if (name)
    {
        delete name;
    }
    this->name = new char[strlen(boy.name) + 1];
    strcpy_s(this->name, strlen(boy.name) + 1, boy.name);
    this->age = boy.age;
    this->salary = boy.salary;
    //this->id = boy.id;  // 根据需求决定要不要拷贝id
    return *this;  // 返回引用可以解决连续赋值的问题 例如 boy1 = boy2 = boy3;
}

bool Boy::operator<(const Boy& boy)
{
    if (this->power() < boy.power())
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool Boy::operator>(const Boy& boy)
{
    if (this->power() > boy.power())
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool Boy::operator==(const Boy& boy)
{
    if (this->power() == boy.power())
    {
        return true;
    }
    else
    {
        return false;
    }
}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int Boy::power() const
{
    // 薪资 + (100 - 年龄)* 1000 
    int temp = salary + (100 - age) * 1000;
    return temp;
}

int main()
{
    Boy boy1("小明", 18, 3000);
    Boy boy2("小李", 23, 25000);

    if (boy1 > boy2)
    {
        cout << "选择boy1" << endl;
    }
    else if (boy1 == boy2)
    {
        cout << "旗鼓相当" << endl;
    }
    else
    {
        cout << "选择boy2" << endl;
    }

    return 0;
}

image

八 重载运算符 []

#include <iostream>
#include <sstream>
#include <string>

#define AGE_KEY     "age"
#define SALARY_KEY  "salary"
#define ID_KEY      "id"
#define POWER_KEY   "power"

typedef enum
{
    AGE,
    SALARY,
    ID,
    POWER
}BOY_KEY_TYPE;

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    ~Boy();

    int operator[](const string index);
    int operator[](BOY_KEY_TYPE index);
    string description();
private:
    int power() const;    // 综合能力值
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

int Boy::operator[](const string index)
{
    if (index == AGE_KEY)
    {
        return age;
    }
    else if (index == SALARY_KEY)
    {
        return salary;
    }
    else if (index == ID_KEY)
    {
        return id;
    }
    else if (index == POWER_KEY)
    {
        return power();
    }
    else
    {
        return -1;
    }
}

int Boy::operator[](BOY_KEY_TYPE index)
{
    if (index == AGE)
    {
        return age;
    }
    else if (index == SALARY)
    {
        return salary;
    }
    else if (index == ID)
    {
        return id;
    }
    else if (index == POWER)
    {
        return power();
    }
    else
    {
        return -1;
    }
}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int Boy::power() const
{
    // 薪资 + (100 - 年龄)* 1000 
    int temp = salary + (100 - age) * 1000;
    return temp;
}

int main()
{
    Boy boy1("小明", 18, 3000);
    Boy boy2("小李", 23, 25000);

    cout << "boy1 id:" << boy1[ID_KEY] << " age:" << boy1[AGE_KEY]
        << " salary:" << boy1[SALARY_KEY] << " power:" << boy1[POWER_KEY] << endl;
    cout << "boy2 id:" << boy2[ID] << " age:" << boy2[AGE]
        << " salary:" << boy2[SALARY] << " power:" << boy2[POWER] << endl;

    return 0;
}

image

九 重载运算符 << >>

因为用成员函数重载运算符<< >>使用起来不方便,所以用友元函数进行重载

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    ~Boy();

    //ostream& operator<<(ostream& os) const;  //这种方式用起来很不方便,所以要用友元函数重载<<
    friend ostream& operator<<(ostream& os, const Boy& boy);
    friend istream& operator>>(istream& is, Boy& boy);
    string description();
private:
    int power() const;    // 综合能力值
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

//ostream& Boy::operator<<(ostream& os) const
//{
//    os << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
//    return os;
//}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int Boy::power() const
{
    // 薪资 + (100 - 年龄)* 1000 
    int temp = salary + (100 - age) * 1000;
    return temp;
}

ostream& operator<<(ostream& os, const Boy& boy)
{
    os << "id:" << boy.id << " 姓名:" << boy.name << " 年龄:" << boy.age << " 薪资:" << boy.salary;
    return os;
}

istream& operator>>(istream& is, Boy& boy)
{
    string name;
    is >> name >> boy.age >> boy.salary;
    if (boy.name)
    {
        delete boy.name;
    }
    boy.name = new char[name.length() + 1];
    strcpy_s(boy.name, name.length() + 1, name.c_str());
    return is;
}

int main()
{
    Boy boy("小明", 18, 3000);

    // 用成员函数重载<<
    //cout << boy1;  // cout.operator<<(boy1)
    //boy1 << cout;  // 这种方式用起来很不方便,所以不用成员函数重载<<

    cout << boy << endl;
    cout << "请依次输入姓名,年龄,薪资:";
    cin >> boy;
    cout << boy << endl;

    return 0;
}

image

十 重载类型运算符

10.1 普通类型 --> 类类型

需求:
Boy boy1 = 10000; // 薪资 构造函数Boy(int);
Boy boy2 = "Rock" // 姓名 构造函数 Boy(char *);

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    Boy(int salary);
    Boy(const char* name);
    ~Boy();

    friend ostream& operator<<(ostream& os, const Boy& boy);
    friend istream& operator>>(istream& is, Boy& boy);
    string description();
private:
    int power() const;    // 综合能力值
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::Boy(int salary)
{
    const char* defaultName = "未命名";
    this->name = new char[strlen(defaultName)+1];
    strcpy_s(this->name, strlen(defaultName) + 1, defaultName);
    this->age = 0;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::Boy(const char* name)
{
    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = 0;
    this->salary = 0;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int Boy::power() const
{
    // 薪资 + (100 - 年龄)* 1000 
    int temp = salary + (100 - age) * 1000;
    return temp;
}

ostream& operator<<(ostream& os, const Boy& boy)
{
    os << "id:" << boy.id << " 姓名:" << boy.name << " 年龄:" << boy.age << " 薪资:" << boy.salary;
    return os;
}

istream& operator>>(istream& is, Boy& boy)
{
    string name;
    is >> name >> boy.age >> boy.salary;
    if (boy.name)
    {
        delete boy.name;
    }
    boy.name = new char[name.length() + 1];
    strcpy_s(boy.name, name.length() + 1, name.c_str());
    return is;
}

int main()
{
    /*
    * 需求:
    Boy boy1 = 10000; // 薪资 构造函数Boy(int); 
    Boy boy2 = "Rock" // 姓名 构造函数 Boy(char *);
    */
    Boy boy1 = "小明";
    Boy boy2 = 5000;

    cout << boy1 << endl;
    cout << boy2 << endl;

    return 0;
}

image

10.2 类类型 --> 普通类型

调用特殊的运算符重载函数,类型转换函数,不需要写返回类型
类型转换函数:operator 普通类型 ()

需求:
Boy boy1(“小明”, 28, 10000);
int power = boy1; // power();
char *name = boy1; // “小明”

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    ~Boy();

    friend ostream& operator<<(ostream& os, const Boy& boy);
    friend istream& operator>>(istream& is, Boy& boy);

    // 特殊的运算符重载:类型转换函数,不需要写返回类型
    operator int() const;
    operator char* () const;

    string description();
private:
    int power() const;    // 综合能力值
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

Boy::operator int() const
{
    return power();
}

Boy::operator char* () const
{
    return name;
}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int Boy::power() const
{
    // 薪资 + (100 - 年龄)* 1000 
    int temp = salary + (100 - age) * 1000;
    return temp;
}

ostream& operator<<(ostream& os, const Boy& boy)
{
    os << "id:" << boy.id << " 姓名:" << boy.name << " 年龄:" << boy.age << " 薪资:" << boy.salary;
    return os;
}

istream& operator>>(istream& is, Boy& boy)
{
    string name;
    is >> name >> boy.age >> boy.salary;
    if (boy.name)
    {
        delete boy.name;
    }
    boy.name = new char[name.length() + 1];
    strcpy_s(boy.name, name.length() + 1, name.c_str());
    return is;
}

int main()
{
    Boy boy1("小明", 20, 5000);
    int power = boy1;  //薪资 + (100 - 年龄)* 1000   5000+80*1000 = 85000
    char* name = boy1;

    cout << boy1 << endl;
    cout << "power:" << power << endl;
    cout << "name:" << name << endl;

    return 0;
}

image

10.3 类类型A --> 类类型B

调用对应的只有一个参数【参数的类型就是类类型 A】的构造函数 也可以使用类型转换函数,但是使用对应的构造函数更合适。
实例: 把 Boy 类型,转换为 Man 类型

#include <iostream>
#include <sstream>
#include <string>

//-------------------  Boy 类  ------------------------
#define AGE_KEY     "age"
#define SALARY_KEY  "salary"
#define ID_KEY      "id"
#define POWER_KEY   "power"

typedef enum
{
    AGE,
    SALARY,
    ID,
    POWER
}BOY_KEY_TYPE;

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    Boy(int salary);
    Boy(const char* name);
    ~Boy();

    Boy& operator=(const Boy& boy);  // 注意返回值类型和参数类型
    bool operator<(const Boy& boy);
    bool operator>(const Boy& boy);
    bool operator==(const Boy& boy);
    int operator[](const string index) const;
    int operator[](BOY_KEY_TYPE index) const;

    //ostream& operator<<(ostream& os) const;  //这种方式用起来很不方便,所以要用友元函数重载<<
    friend ostream& operator<<(ostream& os, const Boy& boy);
    friend istream& operator>>(istream& is, Boy& boy);

    // 特殊的运算符重载:类型转换函数,不需要写返回类型
    operator int() const;
    operator char* () const;

    string description();
private:
    int power() const;    // 综合能力值
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::Boy(int salary)
{
    const char* defaultName = "未命名";
    this->name = new char[strlen(defaultName)+1];
    strcpy_s(this->name, strlen(defaultName) + 1, defaultName);
    this->age = 0;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::Boy(const char* name)
{
    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = 0;
    this->salary = 0;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

// 注意返回值类型和参数类型
Boy& Boy::operator=(const Boy& boy)
{
    if (name)
    {
        delete name;
    }
    this->name = new char[strlen(boy.name) + 1];
    strcpy_s(this->name, strlen(boy.name) + 1, boy.name);
    this->age = boy.age;
    this->salary = boy.salary;
    //this->id = boy.id;  // 根据需求决定要不要拷贝id
    return *this;  // 返回引用可以解决连续赋值的问题 例如 boy1 = boy2 = boy3;
}

bool Boy::operator<(const Boy& boy)
{
    if (this->power() < boy.power())
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool Boy::operator>(const Boy& boy)
{
    if (this->power() > boy.power())
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool Boy::operator==(const Boy& boy)
{
    if (this->power() == boy.power())
    {
        return true;
    }
    else
    {
        return false;
    }
}

int Boy::operator[](const string index) const
{
    if (index == AGE_KEY)
    {
        return age;
    }
    else if (index == SALARY_KEY)
    {
        return salary;
    }
    else if (index == ID_KEY)
    {
        return id;
    }
    else if (index == POWER_KEY)
    {
        return power();
    }
    else
    {
        return -1;
    }
}

int Boy::operator[](BOY_KEY_TYPE index) const
{
    if (index == AGE)
    {
        return age;
    }
    else if (index == SALARY)
    {
        return salary;
    }
    else if (index == ID)
    {
        return id;
    }
    else if (index == POWER)
    {
        return power();
    }
    else
    {
        return -1;
    }
}

//ostream& Boy::operator<<(ostream& os) const
//{
//    os << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
//    return os;
//}

Boy::operator int() const
{
    return power();
}

Boy::operator char* () const
{
    return name;
}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int Boy::power() const
{
    // 薪资 + (100 - 年龄)* 1000 
    int temp = salary + (100 - age) * 1000;
    return temp;
}

ostream& operator<<(ostream& os, const Boy& boy)
{
    os << "id:" << boy.id << " 姓名:" << boy.name << " 年龄:" << boy.age << " 薪资:" << boy.salary;
    return os;
}

istream& operator>>(istream& is, Boy& boy)
{
    string name;
    is >> name >> boy.age >> boy.salary;
    if (boy.name)
    {
        delete boy.name;
    }
    boy.name = new char[name.length() + 1];
    strcpy_s(boy.name, name.length() + 1, name.c_str());
    return is;
}

//-------------------  Man 类  ------------------------
class Man
{
public:
    Man(const char* name, int salary);
    Man(const Boy& boy);
    ~Man();

    friend ostream& operator<<(ostream& os, Man& man);
private:
    char* name;
    int salary;
};

Man::Man(const char* name, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    int len = strlen(name) + 1;
    this->name = new char[len];
    strcpy_s(this->name, len, name);
    this->salary = salary;
}

Man::Man(const Boy& boy)
{
    int len = strlen((char*)boy) + 1;  //(char*)boy 调用 operator char* () const;
    this->name = new char[len];
    strcpy_s(this->name, len, (char*)boy);
    this->salary = boy[SALARY];        //boy[SALARY] 调用int Boy::operator[](BOY_KEY_TYPE index) const
}

Man::~Man()
{
    if (name)
    {
        delete name;
    }
}

ostream& operator<<(ostream& os, Man& man)
{
    os << "name:" << man.name << " salary:" << man.salary;
    return os;
}

//--------------------- main 函数 ----------------------------
int main()
{
    Boy boy("小明", 20, 5000);
    Man man = boy;

    cout << boy << endl;
    cout << man << endl;

    return 0;
}

image

十一 常见错误总结

11.1 const导致的异常bug

#include <iostream>
#include <sstream>
#include <string>

//-------------------  Boy 类  ------------------------
#define AGE_KEY     "age"
#define SALARY_KEY  "salary"
#define ID_KEY      "id"
#define POWER_KEY   "power"

typedef enum
{
    AGE,
    SALARY,
    ID,
    POWER
}BOY_KEY_TYPE;

using namespace std;

class Boy
{
public:
    Boy();
    Boy(const char* name, int age, int salary);
    ~Boy();

    int operator[](const string index) const;
    int operator[](int index);

    string description();
private:
    int power() const;    // 综合能力值
    char* name;           // 姓名
    int age;              // 年龄
    int salary;           // 薪资
    int id;               // id
    static int LAST_ID;   // 最后一个id号
};

// 初始化静态数据成员
int Boy::LAST_ID = 0;

Boy::Boy()
{
    name = new char('\0');
    age = 0;
    salary = 0;
    id = ++LAST_ID;
}

Boy::Boy(const char* name, int age, int salary)
{
    if (!name)
    {
        name = "未命名";
    }

    this->name = new char[strlen(name) + 1];
    strcpy_s(this->name, strlen(name) + 1, name);
    this->age = age;
    this->salary = salary;
    this->id = ++LAST_ID;
}

Boy::~Boy()
{
    if (name)
    {
        delete name;
    }
}

int Boy::operator[](const string index) const
{
    if (index == AGE_KEY)
    {
        return age;
    }
    else if (index == SALARY_KEY)
    {
        return salary;
    }
    else if (index == ID_KEY)
    {
        return id;
    }
    else if (index == POWER_KEY)
    {
        return power();
    }
    else
    {
        return -1;
    }
}

int Boy::operator[](int index)
{
    if (index == AGE)
    {
        return age;
    }
    else if (index == SALARY)
    {
        return salary;
    }
    else if (index == ID)
    {
        return id;
    }
    else if (index == POWER)
    {
        return power();
    }
    else
    {
        return -1;
    }
}

string Boy::description()
{
    stringstream ret;
    ret << "id:" << id << " 姓名:" << name << " 年龄:" << age << " 薪资:" << salary;
    return ret.str();
}

int Boy::power() const
{
    // 薪资 + (100 - 年龄)* 1000 
    int temp = salary + (100 - age) * 1000;
    return temp;
}

//--------------------- main 函数 ----------------------------
int main()
{
    const Boy boy("小明", 20, 5000);
    //cout << boy[0] << endl; // 报错 const 对象只能调用const方法,所以这里不能调用 int operator[](int index);

    return 0;
}

我们这里boy定义的是const对象,只能调用对应的const方法,所以这里报错

image

所以:
类的成员函数,如果已经确定不会修改任何数据成员,那么,最好把这个成员函数定义为const函数(int Boy::operator[](int index) const;)

11.2 重载赋值运算符 operator= 的参数问题

  • 赋值运算符的重载,应该使用这种方式: Boy& operator=(const Boy &boy);就是:参数要使用引用!

  • 如果定义成: Boy& operator=(const Boy *boy); 将会没有效果,编译器不会识别为赋值运算符的重载
    比如:boy2 = boy1 时不会调用这个函数

  • 如果定义:Boy& operator=(const Boy boy); 有效果,但是在调用时,会执行参数的传递
    比如:boy2 = boy1;
    就会执行: boy2.operator=(boy1);
    就会执行: const Boy boy = boy1;
    就会执行: Boy 类的拷贝构造函数

    • 执行Boy 类的拷贝构造函数 有两个影响:
      1) 浪费性能
      2) 如果没有自定义的拷贝构造函数,而且这个类又有指针成员时,就会调用自动生成的拷贝构造函数,导致浅拷贝,如果析构函数中,对这个指针指向的内存做了释放,那就导致数据损坏或崩溃!
  • 小结:
    1)赋值运算符的重载,一定要使用引用参数
    2)如果一个类有指针成员,而且使用了动态内存分配,那么一定要定义自己的拷贝构造函数【要使用深拷贝】,避免调用自动生成的拷贝构造函数 因为自动生成的拷贝构造函数,是浅拷贝!

posted @ 2022-05-06 16:03  荒年、  阅读(114)  评论(0编辑  收藏  举报