1. 类的定义

1.1. 语法:

  1. class 类名 {
  2. private:
  3. 成员变量1 //定义成员变量
  4. 成员变量2
  5. --
  6. public:
  7. 成员函数1 //定义/声明成员函数
  8. 成员函数2
  9. };

1.2. 常见的叫法:

  • 成员变量,又称:属性、特征、数据
  • 成员函数,又称:行为、功能、方法

    1.3. 访问控制符:

  • 修饰类成员,用来设置类成员的作用域 | 访问控制符 | 说明 | | —- | —- | | private | 所修饰的类成员称为:
    私有成员,只能在本类的成员函数中被访问。注意默认权限是private | | protected | 所修饰的类成员称为:
    保护成员,除了在本类的成员函数中被访问。在派生类的成员函数内也可以访问。 | | public | 所修饰的类成员称为:
    公有成员,在本类的成员函数之内及外部都可以被访问。外部,包括全局函数和其他类的成员函数。 |

一般将成员变量设置为private,将成员函数设置为public,外界只能通过public成员函数访问private成员变量。

1.4. 示例代码:

  1. #include <iostream>
  2. using namespace std;
  3. class Person {
  4. public:
  5. string name;
  6. bool gender;
  7. int age;
  8. void show(){
  9. cout << "本人名叫" << name << endl;
  10. cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
  11. cout << "年方" << age << "青春无敌"<< endl;
  12. }
  13. };
  14. int main(){
  15. Person ps = {"王大锤", true, 18};
  16. cout << ps.name << endl; // 调用成员变量
  17. ps.show(); // 调用成员函数
  18. return 0;
  19. }

2. 构造函数

2.1. 概念

  • 构造函数是专门用于初始化对象的成员变量
    • 对象的初始化就是给成员变量赋初值
  • 构造函数无返回值,函数名与类名相同,一般是public
  • 构造函数在创建对象时自动被调用,每个对象的创建都会调用构造函数
    • private和protected成员变量无法使用初值列表{…}初始化
  • 示例代码: ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

int main(){ //Person ps = {“王大锤”, true, 18}; // error Person ps(“王大锤”, true, 18); // 自动调用Person(“王大锤”, true, 18); ps.show(); // 调用成员函数 return 0; }

  1. <a name="uIbqv"></a>
  2. ## 2.2. 构造函数的重载
  3. - 构造函数可以重载,一个类可以有多个构造函数
  4. - 创建对象时,根据传入的参数自动调用相应的构造函数
  5. - 示例:
  6. ```cpp
  7. Person(string name_, int age_);
  8. Person(string name_);
  9. Person( );
  10. Person ps("tom", 18);
  11. Person ps("tom");
  12. Person ps;
  • 默认的无参构造函数
    • 如果没有自定义构造函数,编译器会在编译时提供一个默认的无参构造函数,其定义为:Person(){ }
    • 如果用户定义了任意一个构造函数,则编译器将不再提供默认的无参构造函数
  • 示例代码: ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } Person(string name, bool gender){ name = name; gender = gender; cout << “Person(string, bool)” << endl; } Person(string name){ name = name; cout << “Person(string)” << endl; } Person(){ cout << “Person()” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

int main(){ Person ps(“王大锤”, true, 18); // 自动调用Person(“王大锤”, true, 18); ps.show();

  1. cout << "------------------" << endl;
  2. Person ps1("tom", true);
  3. ps1.show();
  4. cout << "------------------" << endl;
  5. Person ps2("jerry");
  6. ps2.show();
  7. cout << "------------------" << endl;
  8. Person ps3;
  9. ps3.show();
  10. return 0;

}

<a name="aFaW2"></a>
## 2.3. 构造函数的初始化列表
<a name="wdgX8"></a>
### 2.3.1. 构造函数对成员变量赋初值有两种方式:

- 在函数体内赋值
```cpp
Person::Person(string name_, int age_, bool gender_) {
    name = name_;
    age = age_;
    gender = gender_;
}
  • 在初始化列表中赋初值

    Person::Person(string name_, int age_, bool gender_)
      :name(name_), age(age_), gender(gender_){
    }
    
  • 以下情况必须使用初始化列表对成员变量进行赋值

    • 含有const成员 (因为const成员定义时必须初始化)
    • 含有引用型成员 (因为引用型成员定义时必须初始化)

      2.3.2. 示例代码

      ```cpp

      include

      using namespace std;

class Person{ private: string name; const bool gender; // 只能在初始化列表中初始化 int& age; // 只能在初始化列表中初始化 public: Person(string name, bool gender, int age) : name(name), gender(gender), age(age){ cout << “Person(string, bool, int)” << endl; } / Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } / void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

int main(){ Person ps(“王大锤”, true, 500); // 自动调用Person(“王大锤”, true, 18); ps.show(); // 调用成员函数 return 0; }

<a name="U4BrQ"></a>
# 3. 拷贝构造函数
<a name="lt5HB"></a>
## 3.1. 概念

- 拷贝构造函数是构造函数的一种,原型固定如下: `类名 (const 类名& that);`
   - 如果没有自定义拷贝构造函数,编译器会提供默认的拷贝构造函数
   - 如果自定义了拷贝构造函数,编译器不会提供默认的拷贝构造函数
- 默认拷贝构造函数的定义如下:
```cpp
// 定义方式,二选一
// 1. 类内定义
类名 (const 类名& that) {
    member1 = that.member1;
    member2 = that.member2;
    ...
}

// 2. 类外定义
类名::类名 (const 类名& that) {
    member1 = that.member1;
    member2 = that.member2;
    ...
}

// 一般不需要自定义拷贝构造函数,使用默认的即可

3.2. 拷贝构造函数的使用场景

  • 定义对象时 ```cpp Person ps(“孙悟空”, true, 800);

Person ps1(ps); Person ps1 = ps; // 此处调用的是拷贝构造函数而非赋值运算符


- 将对象的副本作为函数参数传递时
```cpp
f(Person ps) {

}
f(tom);
  • 将对象作为函数返回值时
    Person f() {
      return ps;
    }
    // g++默认会优化,使用-fno-elide-consturctors关闭优化才能看到效果
    

    3.3. 技巧

    使用引用类型做参数和返回值,可以避免调用拷贝构造函数,提高效率

3.4. 示例代码

#include <iostream>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int age;
    public:
        Person(string name_, bool gender_, int age_){
            name = name_;
            gender = gender_;
            age = age_;
            cout << "Person(string, bool, int)" << endl;
        }
        Person(const Person& that){
            name = that.name;
            gender = that.gender;
            age = that.age;
            cout << "Person(const Person&)" << endl;
        }
        void show(){
            cout << "本人名叫" << name << endl;
            cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
            cout << "年方" << age << "青春无敌"<< endl;
        }
};

void f(Person pn){
    pn.show();
    cout << "f(Person ps)" << endl;
}

Person g(){
    Person ps("猪八戒", true, 21);
    cout << "g()" << endl;
    return ps;
}

int main(){
    Person ps("孙悟空", true, 800); 
    ps.show(); 

    cout << "-----------------" << endl;
    Person ps1(ps); // 调用了拷贝构造
    ps1.show();

    cout << "-----------------" << endl;
    Person ps2 = ps; // 调用了拷贝构造
    ps2.show();

    cout << "-----------------" << endl;
    f(ps); // 传对象时调用了拷贝构造

    cout << "-----------------" << endl;
    Person ps3 = g(); // 返回对象时调用了拷贝构造

    return 0;
}

4. 析构函数

4.1. 概念

  • 析构函数用于在销毁对象前执行一些清理工作,释放资源
  • 析构函数无返回值,函数名为 ~类名,无参数,不能被重载
  • 当对象销毁释放时被自动调用,一般不直接调用
    • { Person ps; }// 代码块结束时,析构函数被自动调用
  • 如果没有自定义析构函数,编译器会提供一个默认的析构函数:~Person( ){ }
  • 静态存储区和栈区定义的对象,先构造后析构

    4.2. 示例代码

    ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: ~Person(){ cout << name << “ : ~Person()” << endl; } Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << name << “ : Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

Person ps2(“王锤锤”, true, 17); // 全局对象

int main(){ { Person ps(“王大锤”, true, 18); // 自动调用Person(“王大锤”, true, 18); ps.show(); // 调用成员函数

    Person ps1("王小锤", true, 16);
}

cout << "over" << endl;
//ps2.~Person(); // 不要去主动调用析构函数
return 0;

}

<a name="WMS9V"></a>
# 5. 临时对象和explicit

- 临时对象:
   - 直接调用构造函数,创建的对象是临时对象.  Person(1000);
   - 临时对象的生命周期是创建该临时对象的表达式结束";"时
- `explicit`,用来修饰单参构造函数,禁止隐式构造
- 示例代码:
```cpp
#include <iostream>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int age;
    public:
        Person(string name_, bool gender_, int age_){
            name = name_;
            gender = gender_;
            age = age_;
            cout << "Person(string, bool, int)" << endl;
        }
        explicit Person(int age_){
            age = age_;
            cout << "Person(int)" << endl;
        }
        Person(){
        }

        void show(){
            cout << "本人名叫" << name << endl;
            cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
            cout << "年方" << age << "青春无敌"<< endl;
        }
};

int main(){
    Person("tom", true, 18); // 临时对象
    Person ps; // 调用了Person()
    ps.show();

    cout << "-----------------" << endl;
    ps = Person("jerry", true, 19);
    ps.show();

    cout << "-----------------" << endl;
    ps = Person(10000); // 显式调用构造函数
    ps.show();

    cout << "-----------------" << endl;
    //ps = 88888 ; // 88888 -> Person(88888); explicit禁止隐式构造
    ps.show();

    return 0;
}

6. new/delete对象

  • 可以使用newdelete在堆区创建销毁对象
  • 语法:

    Person *p = new Person;        //调用无参构造    ===> 创建object
    Person *p = new Person("tom"); // 调用有参构造
    Person *p = new Person(ps);    // 调用拷贝构造
    delete p; // 销毁object
    
  • 与malloc( )和free( )的区别;

    • new分配内存并调用构造函数
    • malloc只分配内存,不做其他的事情
    • delete释放内存并调用析构函数
    • free只释放内存,不做其他的事情
  • 示例代码: ```cpp

    include

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: ~Person(){ cout << name << “ : ~Person()” << endl; } Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } Person(string name, bool gender){ name = name; gender = gender; cout << “Person(string, bool)” << endl; } Person(string name){ name = name; cout << “Person(string)” << endl; } Person(){ cout << “Person()” << endl; } Person(const Person& that){ name = that.name; gender = that.gender; age = that.age; cout << “Person(const person&)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; } };

int main(){ Person* p = new Person; // 无参构造 p->show();

cout << "--------------------" << endl;
Person* p1 = new Person("tom");
p1->show();

cout << "--------------------" << endl;
Person* p2 = new Person("jerry", true);
p2->show();

cout << "--------------------" << endl;
Person* p3 = new Person("bluce", true, 18);
p3->show();

cout << "--------------------" << endl;
Person* p4 = new Person(*p3);
p4->show();

cout << "here1" << endl;
cout << "--------------------" << endl;
Person* p5 = (Person*)malloc(sizeof(Person));
//p5->show(); // 使用malloc为对象申请内存不安全
cout << "here2" << endl;

cout << "============" << endl;
free(p5);
cout << "============" << endl;

delete p; p = NULL;
delete p1; p1 = NULL;
delete p2; p2 = NULL;
delete p3; p3 = NULL;
delete p4; p4 = NULL;

return 0;

}

<a name="vyKHC"></a>
# 7. set函数和get函数

- 提出问题:
   - 类的private成员变量的值无法在成员函数之外被访问.
- 解决问题:
   - 可以通过类的public成员函数间接访问类的private成员变量
      - 将专门用来修改类的成员变量的成员函数称为set( )函数
      - 将专门获取类的成员变量值的成员函数称为get( )函数
- 示例代码:
```cpp
#include <iostream>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int age;
    public:
        Person(string name_, bool gender_, int age_){
            name = name_;
            gender = gender_;
            age = age_;
            cout << "Person(string, bool, int)" << endl;
        }
        void show(){
            cout << "本人名叫" << name << endl;
            cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
            cout << "年方" << age << "青春无敌"<< endl;
        }

        void setName(string name_){
            name = name_;
        }
        string getName(){
            return name;
        }
};

int main(){
    Person ps("王大锤", true, 18); 
    ps.setName("王帅");
    ps.show();

    cout << ps.getName() << endl;
    return 0;
}

8. 类的声明和实现分开

  • 细节
    • 在实际开发中类的声明和实现是分开的
    • 声明部分,包含成员变量的声明和成员函数的声明;实现部分,包含成员函数的实现。
    • 声明放在.h文件中,实现放在.cpp文件中
  • 示例代码
    • person.cpp ```cpp

      include “person.h”

      include

Person::Person(string name) { name = name; }

void Person::show() { cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “, 青春无敌” << endl; }


   - person.h
```cpp
#ifndef __PERSON_H_
#define __PERSON_H_

#include <string>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int age;
    public:
        Person(string name_);
        void show();
};

#endif
  • main.cpp ```cpp

    include “person.h”

    include

int main(int argc, const char** argv) { Person ps1(“tom”); ps1.show(); return 0; }

<a name="sDJGN"></a>
# 9. this指针
<a name="IEWn6"></a>
## 9.1. 概念

- 类的成员函数有一个隐藏的形参,参数类型为类名 *,参数名为this
- this的作用域为成员函数的函数体(成员函数内部)
- this指针保存的是当前对象的地址
- 对于普通的成员函数,this指针指向调用该成员函数的对象
- 对于构造函数,this指针指向正在被创建的对象
<a name="ZwPo0"></a>
## 9.2. this指针的应用场景

- 调用成员变量,避免名字冲突
   - `this->name = name; // name是形参`
- 从成员函数返回调用对象
   - `Person f( ){ return *this;}`
<a name="kVoeg"></a>
## 9.3. 示例代码
```cpp
#include <iostream>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int age;
    public:
        Person(string name, bool gender, int age){
            this->name = name;
            this->gender = gender;
            //age = age;
            this->age = age;
            cout << "age = " << age << endl;
            cout << "this->age = " << this->age << endl;
            cout << "Person(string, bool, int)" << endl;
        }
        void show(){
            cout << "本人名叫" << name << endl;
            cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
            cout << "年方" << age << "青春无敌"<< endl;
        }
        Person f(){
            return *this;
        }
};

int main(){
    Person ps("王大锤", true, 18); 
    ps.show();

    cout << "-----------" << endl;
    ps.f().show();
    return 0;
}

10. const成员函数

10.1. 格式:

返回类型 函数名(形参表)const {

}

10.2. 注意项:

  • 通常将不打算修改成员变量的成员函数声明为const
  • const成员函数不能修改成员变量
    • 对成员变量只读
  • const成员函数不能调用非const成员函数,只能调用const成员函数
  • 同名同参的成员函数,const形式与非cosnt形式,形成重载关系

    10.3. 示例代码:

    ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; }

    void setName(string name){ // 不能被声明为const
        this->name = name;
    }
    string getName()const{
        cout << "getName()const" << endl;
        return name;
    }
    string getName(){
        cout << "getName()" << endl;
        return name;
    }

};

int main(){ const Person ps(“tom”, true, 18); // 只能调用const成员函数 //ps.show(); //error, 不能调用非const成员函数 cout << ps.getName() << endl;

Person ps1("jerry", true, 19); // 优先调用非const成员函数,也可以调用const成员函数
cout << ps1.getName() << endl;
return 0;

}

<a name="pU6l9"></a>
# 11. const对象

- `const Person p1;`, Person是一个类。
- const对象只能调用const成员函数,不能调用非const成员函数
- const形成重载时,非const对象优先选择非const版成员函数
- const修饰引用类型对象时可以保证对象不被修改
<a name="wjUvS"></a>
# 12. static成员
<a name="E3G4H"></a>
## 12.1. static成员变量

- 用于描述所有对象的共性,被所有对象共享
- static成员变量在类内声明,在类外定义和初始化,保存在静态区
- 语法:
```cpp
class Person {
    static int counter; // 声明
};

int Person::counter = 0; // 定义并初始化,加类名作用域解析符(Person::),不带static

12.2. static成员函数

  • 特性
    • 静态成员函数用于访问静态成员变量
    • 静态成员函数没有this指针,因此不能访问非静态成员(变量和函数)
    • 静态函数可以通过类名或者对象名调用
  • 语法
    class Person {
      static void f() { }
    };
    

    12.3. 示例代码

    ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; static int counter; // 在类内声明 public: Person(string name, bool gender, int age){ name = name; gender = gender; age = age; counter++; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; }

    static int getCounter(){
        //cout << this->name << endl; //static成员函数没有this指针
        return counter;
    }

};

int Person::counter = 0; // 在类外定义和初始化,存在静态区

void f(){ Person p1 = new Person(“sunwukong”, true, 500); Person p2 = new Person(“zhubajie”, true, 300); }

int main(){ f(); cout << Person::getCounter() << endl; Person ps(“tom”, true, 18); cout << ps.getCounter() << endl;

return 0;

}

<a name="t7pFY"></a>
# 13. 深拷贝和浅拷贝
> 深拷贝和浅拷贝指的是拷贝构造函数的两种不同的实现

<a name="aRhXS"></a>
## 13.1. 浅拷贝

- 默认的拷贝构造函数的实现称为浅拷贝
- 浅拷贝是简单地逐个成员按值复制
- 浅拷贝存在的问题:
   - 当类中包含指针型成员变量且使用该指针在构造函数中申请动态内存时,浅拷贝复制指针型成员变量的值,两个指针变量指向同一块内存,销毁对象时,多个对象的析构函数会delete同一块内存,造成重复释放。
<a name="aQmYJ"></a>
## 13.2. 深拷贝

- 深拷贝可以解决浅拷贝存在的问题
- 深拷贝使用指针变量重新new一块内存,将原指针变量所指向内存中的值复制到新内存,将两个指针变量指向不同的内存
- 如果需要深拷贝,则需自定义拷贝构造函数,实现深拷贝
<a name="mmBbJ"></a>
## 13.3. 示例代码
```cpp
#include <iostream>
using namespace std;

class Person{
    private:
        string name;
        bool gender;
        int* age;
    public:
        ~Person(){
            delete age;
            cout << "~Person()" << endl;
        }
        Person(string name_, bool gender_, int age_){
            name = name_;
            gender = gender_;
            age = new int;
            *age = age_;
            cout << "Person(string, bool, int)" << endl;
        }
        Person(const Person& that){
            name = that.name;
            gender = that.gender;

            //age = that.age;             // 浅拷贝

            age = new int; // 重新new内存  // 深拷贝
            *age = *that.age; // 复制同样的年龄
            cout << "Person(const Person&)" << endl;
        }
        void show(){
            cout << "本人名叫" << name << endl;
            cout << (gender == true ? "帅哥一枚" : "美女一枚") << endl;
            cout << "年方" << age << "青春无敌"<< endl;
        }
};

int main(){
    Person ps("tom", true, 18);
    Person ps1(ps);

    return 0;
}

14. 成员对象的构造和析构

  • 特性:
    • 成员对象的析构在当前对象的构造之前
    • 必须调用成员对象的类的构造函数来构造成员对象
    • 成员对象的构造必须在类构造函数的初始化列表中进行
    • 多个成员对象的构造顺序与成员对象在类中的声明顺序一致,与其在初始化列表中的顺序无关
    • 成员对象的析构在当前对象的析构之后
  • 示例代码: ```cpp

    include

    using namespace std;

class Book{ private: string bookName; public: ~Book(){ cout << bookName << “ : ~Book()” << endl; } Book(){ cout << “Book()” << endl; } Book(string bookName){ this->bookName = bookName; cout << “Book(string)” << endl; } Book(const Book& that){ bookName = that.bookName; cout << “Book(const Book&)” << endl; } string getBookName(){ return bookName; } };

class Person{ private: string name; bool gender; int age; Book book; public: ~Person(){ cout << “~Person()” << endl; } Person(string name, bool gender, int age) : book(){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } Person(string name, bool gender, int age, string bookName) : book(bookName){ name = name; gender = gender; age = age; cout << “Person(string, bool, int, string)” << endl; } Person(string name, bool gender, int age, Book book) : book(book){ name = name; gender = gender; age = age; cout << “Person(string, bool, int, string)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; cout << “我有一本好书,名字是” << book.getBookName() << endl; } };

int main(){ Person ps(“tom”, true, 18); ps.show(); cout << “——————“ << endl; Person ps1(“jerry”, true, 19, “c++”); ps1.show();

cout << "------------" << endl;
Book book("红楼梦");
Person ps2("rose", false, 20, book);
ps2.show();
return 0;

}

<a name="CylxM"></a>
# 15. 友元函数

- 特性:
   - 以friend关键字声明的全局函数是友元函数
   - **_友元函数可以访问类中的所有成员,不限于public成员_**
   - 在_类内定义_,也可以在_类外定义、类内声明_,在**类外定义时不用加_类名作用域解析符(类名::)和friend关键字_**
```cpp
class X {
    friend void f(Person& other) { }  // 类内定义
    // or
    friend void f(Person& other); // 类内声明
};

// 类外定义
void Person::f(Person& other) { } // error
void f(Person& other) { } // ok
  • 示例代码: ```cpp

    include

    using namespace std;

class Person{ private: string name; bool gender; int age; public: Person(string name, bool gender, int age){ name = name; gender = gender; age = age; cout << “Person(string, bool, int)” << endl; } void show(){ cout << “本人名叫” << name << endl; cout << (gender == true ? “帅哥一枚” : “美女一枚”) << endl; cout << “年方” << age << “青春无敌”<< endl; }

    friend void borrow(const Person& ps);

};

void borrow(const Person& ps){ cout << ps.name << “借我100元” << endl; // 可以访问私有成员 }

int main(){ Person ps(“tom”, true, 18); borrow(ps); return 0; }

<a name="P8aJy"></a>
# 16. 运算符重载
<a name="RV6nX"></a>
## 16.1. 运算符函数

- 在C++中某些运算符有对应的函数形式,执行运算符等同于调用相应的运算符函数
```cpp
str1 + str2 <=> operator+(str1, str2)
str1 > str2 <=> operator>(str1, str2)
  • 一个对象能够使用某种运算符,取决于该对象的类是否定义了相应的运算符函数

    str1 - str2 // error, 没有相应的operator-(str1, str2)
    
  • 运算符的函数名为operator#,运算符的操作数对应的函数参数,运算符的运算结果对应函数的返回值

  • 示例代码: ```cpp

    include

    include

    using namespace std;

int main(int argc, const char **argv){ string str1 = “hello”; string str2 = “world”;

cout << str1 + str2 << endl;
cout << operator+(str1, str2) << endl;

cout << (str1 > str2) << endl;
cout << operator>(str1, str2) << endl;

//str1 - str2; // error
//operator-(str1, str2); // error
return 0;

}

<a name="CV6wT"></a>
## 16.2. 运算符重载概念

- 运算符重载是指重载运算符函数,将运算符应用于自定义类型
- 类的运算符函数可以是友元函数也可以是成员函数
- 双目运算符:
   - 一般重载为友元函数,操作数与函数参数对应如下:
> L#R <=> operator#(L, R) ==> L操作数传给L参数,R操作数传给R参数

- 单目运算符:
   - 一般重载为成员函数,操作数与函数参数对应如下:
> #0 <=> 0.operator#() ==> 0操作数传给R参数

<a name="Zj6kx"></a>
## 16.3. 常见运算符的重载
> 假设类名为 X
> class X {
>   ...
> };

<a name="uqkrf"></a>
### 16.3.1. 输出<<

- 想要正常输出自定义类型的对象,必须重载<<运算符,一般重载为友元函数
```cpp
cout是ostream类的对象,代表标准输出设备,cout<< obj <==> operator<<(cout, obj)

friend ostream& operator<<(ostream& out, const X& x)

16.3.2. 输入>>

  • 一般重载为友元函数 ```cpp cin是istream类的对象,代表标准输入设备,cin>> obj <==> operator>>(cin, obj)

friend istream& operator>>(istream& in, X&x)

<a name="k9Yaz"></a>
### 16.3.3. 加法+和乘法*

- 一般重载为友元函数
```cpp
friend X operator+(const X& x1, const X& x2)

friend X operator*(const X& x1, const X& x2)

16.3.4. 前++

  • 一般重载为成员函数 ```cpp X opeartor++()

前++返回新值

<a name="nLdr5"></a>
### 16.3.5. 后++

- 一般重载为成员函数
```cpp
X opeartor++(int)

后++返回旧值

后++使用int哑元

16.3.6. 赋值=

  • 只能重载为成员函数
  • 原型为:X& operator=(const X& that)
  • 如果没有自定义赋值运算符函数,编译器会提供一个默认的
  • 默认赋值运算符的实现如下: ```cpp X& operator=(const X& that) { member1 = that.member1; member2 = that.member2; … }

注意排除自赋值: if (this == &that) return *this;


- 深度赋值:
```cpp
当类中包含指针型成员变量且用该指针变量申请动态内存时,需要重载赋值运算符,将动态内存中的内容赋值过来,而非简单的内存地址赋值
p = that.p; // 错误做法,地址值赋值会存在指向同一块内存
*p = *(that.p); // 正确做法,把地址值对应空间内容赋值过去

16.3.7. 示例代码:

  • demo1 ```cpp

    include

    using namespace std;

// 分数处理类 class FS{ private: int fz; int fm; public: FS(){ cout << “FS()” << endl; }

    FS(int fz, int fm){
        this->fz = fz;
        this->fm = fm;
        cout << "FS(int, int)" << endl;
    }

    friend ostream& operator<<(ostream& out, const FS& fs){
        out << fs.fz << "/" << fs.fm;
        return out;
    }

    friend istream& operator>>(istream& in, FS& fs){
        //fs.fz;
        //fs.fm;
        char ch;
        in >> fs.fz >> ch >> fs.fm;
        return in;
    }

    friend FS operator+(const FS& fs1, const FS& fs2){
        FS fs;
        fs.fz = fs1.fz * fs2.fm + fs2.fz * fs1.fm;
        fs.fm = fs1.fm * fs2.fm;
        return fs;
    }

    friend FS operator*(const FS& fs1, const FS& fs2){
        FS fs;
        fs.fz = fs1.fz * fs2.fz;
        fs.fm = fs1.fm * fs2.fm;
        return fs;
    }

    FS operator++(){ // 前++
        fz = fz + fm;
        //fm = fm;
        cout << "operator++()" << endl;
        return *this;
    }

    FS operator++(int){ // 后++
        FS old = *this;
        fz = fz + fm;
        cout << "operator++(int)" << endl;
        return old;
    }

};

int main(int argc, const char** argv){ string str1 = “hello world”; cout << str1; // operator<<(cout, str1); cout << endl; operator<<(cout, str1); // string类中定义的友元函数 cout << endl;

//cin >> str1; // operator>>(cin, str1);
operator>>(cin, str1);
cout << str1 << endl;

FS fs(1, 2);
cout << "fs : " << fs; // operator<<(cout, fs);
cout << endl;

FS fs1;
cout << "fs1 : " << fs1;
cout << endl;

cin >> fs1; // operator>>(cin, fs1);
cout << "fs1 : " << fs1;
cout << endl;

cout << "fs + fs1 : " << fs + fs1 << endl;
cout << "fs * fs1 : " << fs * fs1 << endl;
cout << "++fs : " << ++fs << endl; //fs.operator++();
cout << "fs1++ : " << fs1++ << endl; //fs1.operator++(888);
cout << "fs1 : " << fs1 << endl;
return 0;

}


- demo2
```cpp
#include <iostream> 
using namespace std;

class FS{
    private:
        int fz;
        int* fm;
    public:
        ~FS(){
            delete fm;
        }

        FS(){
            cout << "FS()" << endl;
            fm = new int;
        }

        FS(int fz, int fm){
            this->fz = fz;
            this->fm = new int;
            *this->fm = fm;
            cout << "FS(int, int)" << endl;
        }

        friend ostream& operator<<(ostream& out, const FS& fs){
            out << fs.fz << "/" << *fs.fm;
            return out;
        }
/*
           // 浅赋值
        FS& operator=(const FS& other){
            if(&other == this){ // 排除自赋值
                return *this;
            }
            fz = other.fz;
            fm = other.fm; 
            cout << "operator=(const FS&)" << endl;
            return *this;
        }
        */

        // 深赋值
        FS& operator=(const FS& other){
            if(&other == this){
                return *this;
            }
            fz = other.fz;
            *fm = *other.fm;
            cout << "operator=(const FS&)" << endl;
            return *this;
        }
};

int main(int argc, const char** argv){
    FS fs(1, 2);
    FS fs1;
    fs1 = fs;
    cout << "fs1 : " << fs1 << endl;
    return 0;
}

16.4. 运算符重载的原则

  • 不可臆造运算符
  • 保持重载运算符的自然含义
  • 操作数中至少一个是自定义类型,否则无重载必要
  • 运算符原有操作数的个数、优先级和结合性不能改变

    16.5. 不能被重载的运算符(6个)

    | 作用域解析运算符 | :: | | —- | —- | | 条件运算符 | ? : | | 直接成员运算符 | . | | 成员(指针)解除引用运算符 | .* | | 获取长度 | sizeof | | 获取类型信息 | typeid |

16.6. 只能重载为成员函数的运算符(4个)

  • 赋值运算符及复合赋值运算符 =,+=, -=, *=, /= 等等
  • 下标运算符 [ ]
  • 括号运算符( )
  • 间接成员运算符->