• C++ Day04 this指针,友元函数,重载


    this指针

    概念

    谁调用 this 所在的函数 ,this 就存储谁的地址

    特点

    1, 在当前类的非静态成员函数中调用本类非静态成员时 , 默认有 this 关键字
    2, 静态成员函数 , 没有 this 指针。
    示例
    1. #include
    2. #include
    3. using namespace std;
    4. class Stu{
    5. private:
    6. char name[50];
    7. char sex[10];
    8. int age;
    9. public:
    10. Stu(){}
    11. Stu(char *n,char *s,int a):age(a)
    12. {
    13. strcpy(name,n);
    14. strcpy(sex,s);
    15. }
    16. void test01()
    17. {
    18. //以下两句代码效果相同
    19. //证明本类函数中调用本类成员默认使用this关键字
    20. cout << this->name << endl;
    21. cout << name << endl;
    22. }
    23. static void test02()
    24. {
    25. //报错,因为静态函数中没有this
    26. //cout << name << endl;
    27. }
    28. };
    29. int main(int argc, char *argv[])
    30. {
    31. Stu s("张三","男",18);
    32. s.test01();
    33. s.test02();
    34. return 0;
    35. }

    使用场景

    1, 局部变量与成员变量重名时 , 使用 this 区分
    2, 调用本类其他成员 , 此时 this 可以省略不写
    示例
    1. #include
    2. #include
    3. using namespace std;
    4. class Stu{
    5. private:
    6. char name[50];
    7. char sex[10];
    8. int age;
    9. public:
    10. Stu(){}
    11. Stu(char *name,char *sex,int age)
    12. {
    13. //当局部变量与成员变量重名,使用this区分
    14. strcpy(this->name,name);
    15. strcpy(this->sex,sex);
    16. this->age = age;
    17. }
    18. void print_info()
    19. {
    20. //调用本类成员变量
    21. cout << this->name << endl;
    22. cout << this->sex << endl;
    23. cout << this->age << endl;
    24. }
    25. void test()
    26. {
    27. //调用本类成员函数
    28. this->print_info();
    29. }
    30. };
    31. int main(int argc, char *argv[])
    32. {
    33. Stu s("张三","男",18);
    34. s.print_info();
    35. s.test();
    36. return 0;
    37. }

    实例

    使用 :*this 完成链式编程
    1. #include
    2. #include
    3. using namespace std;
    4. class Stu{
    5. private:
    6. char name[50];
    7. char sex[10];
    8. int age;
    9. public:
    10. Stu(){}
    11. Stu(char *name,char *sex,int age)
    12. {
    13. strcpy(this->name,name);
    14. strcpy(this->sex,sex);
    15. this->age = age;
    16. }
    17. void print_info()
    18. {
    19. cout << this->name << endl;
    20. cout << this->sex << endl;
    21. cout << this->age << endl;
    22. }
    23. Stu& eat(char *foodName)
    24. {
    25. cout << name << "吃" << foodName << endl;
    26. return *this;
    27. }
    28. };
    29. int main(int argc, char *argv[])
    30. {
    31. Stu s("张三","男",18);
    32. s.eat("凉皮").eat("肉夹馍").eat("甑糕");
    33. return 0;
    34. }

    const修饰成员函数(了解)

    特点

            const修饰的成员函数内部不能对成员数据写操作, mutable 修饰的成员数据 除外。

    实例

    1. class Stu{
    2. private:
    3. char name[50];
    4. char sex[10];
    5. int age;
    6. mutable int score;
    7. public:
    8. Stu(){}
    9. Stu(char *name,char *sex,int age)
    10. {
    11. strcpy(this->name,name);
    12. strcpy(this->sex,sex);
    13. this->age = age;
    14. }
    15. void print_info()
    16. {
    17. cout << this->name << endl;
    18. cout << this->sex << endl;
    19. cout << this->age << endl;
    20. }
    21. Stu& eat(char *foodName)
    22. {
    23. cout << name << "吃" << foodName << endl;
    24. return *this;
    25. }
    26. void test() const
    27. {
    28. //age = 10;//错误
    29. score = 99;//正确
    30. }
    31. };

    友元函数(重要)

    概述

    关键字 :friend
    可以声明 :
            1,全局函数
            2,成员函数
            3,类
    注意 :
            友元打破c++ 的封装性。一般用于运算符重载

    全局友元函数

    特点

    可以访问其友元类的任意成员 , 包括私有成员

    步骤

    1, 在定义并实例全局函数
    2, 在类中声明步骤 1 中的函数为友元函数
    3, 步骤 1 中定义的函数 , 可以访问步骤 2 中定义的类中的所有成员

    示例

    1. #include
    2. #include
    3. using namespace std;
    4. class Stu{
    5. friend void test(Stu &stu);
    6. private:
    7. char name[50];
    8. char sex[10];
    9. int age;
    10. public:
    11. Stu(){}
    12. Stu(char *name,char *sex,int age)
    13. {
    14. strcpy(this->name,name);
    15. strcpy(this->sex,sex);
    16. this->age = age;
    17. }
    18. void print_info()
    19. {
    20. cout << this->name << endl;
    21. cout << this->sex << endl;
    22. cout << this->age << endl;
    23. }
    24. private:
    25. void eat(char *foodName)
    26. {
    27. cout << name << "吃" << foodName << endl;
    28. }
    29. };
    30. void test(Stu& stu)
    31. {
    32. //调用友元类的私有属性
    33. cout << stu.name << endl;
    34. cout << stu.sex << endl;
    35. cout << stu.age << endl;
    36. //调用友元类的私有函数
    37. stu.eat("大嘴巴子");
    38. }
    39. int main(int argc, char *argv[])
    40. {
    41. Stu s("张三","男",18);
    42. test(s);
    43. return 0;
    44. }

    成员友元函数

    特点

            可以访问其友元类的任意成员, 包括私有成员

    注意       

    1, 成员函数作为友元 那么成员函数所在的类 必须定义到最上方
    2, 成员函数所在的类的所有成员函数 必须在两个类的下方实现

    步骤

            1,定义 B , 但不现实
            2,定义成员函数 A1 所在的类 A, 但其中只定义该成员函数 A1
            3,实现 B , 并在其中声明成员函数 A1 为友元函数
            4,实现成员函数

    示例

    1. #include
    2. #include
    3. using namespace std;
    4. //定义B类,但是没有实现
    5. class B;
    6. class A{
    7. public:
    8. void test(B& b);
    9. };
    10. class B{
    11. friend void A::test(B& b);
    12. private:
    13. int a;
    14. public:
    15. B(int a)
    16. {
    17. this->a = a;
    18. }
    19. private:
    20. void print_B()
    21. {
    22. cout << "a = " << a << endl;
    23. }
    24. };
    25. void A::test(B& b)
    26. {
    27. cout << b.a << endl;
    28. b.print_B();
    29. }
    30. int main(int argc, char *argv[])
    31. {
    32. A a;
    33. B b(10);
    34. a.test(b);
    35. return 0;
    36. }

    整个类作为友元函数

    特点

            在B 中声明 A B 的友元类 , 此时 A 中任意成员函数中皆可直接访问 B 中的成员

    步骤        

    1, 定义 B
    2, 定义并实现 A , 其中函数只定义不实现
    3, 实现 B , 在其中声明 A 类为友元类
    4, 实现 A 类中的成员函数

    示例

    1. #include
    2. #include
    3. using namespace std;
    4. //定义B类,但是没有实现
    5. class B;
    6. class A{
    7. public:
    8. void test01(B& b);
    9. void test02(B& b);
    10. };
    11. class B{
    12. friend class A;
    13. private:
    14. int a;
    15. public:
    16. B(int a)
    17. {
    18. this->a = a;
    19. }
    20. private:
    21. void print_B()
    22. {
    23. cout << "a = " << a << endl;
    24. }
    25. };
    26. void A::test01(B& b)
    27. {
    28. cout << "test01" << endl;
    29. cout << b.a << endl;
    30. b.print_B();
    31. }
    32. void A::test02(B& b)
    33. {
    34. cout << "test02" << endl;
    35. cout << b.a << endl;
    36. b.print_B();
    37. }
    38. int main(int argc, char *argv[])
    39. {
    40. A a;
    41. B b(10);
    42. a.test01(b);
    43. cout << "--------------------" << endl;
    44. a.test02(b);
    45. return 0;
    46. }

    注意

    1, 友元关系不能被继承。
    2, 友元关系是单向的,类 A 是类 B 的朋友,但类 B 不一定是类 A 的朋友。
    3, 友元关系不具有传递性。类 B 是类 A 的朋友,类 C 是类 B 的朋友,但类 C 不一
    定是类 A 的朋友

    实例

    说明

    遥控器类的对象可以操作电视机类对象的成员
    所以遥控器类是电视机类的友元类

    代码

    1. #include
    2. #include
    3. using namespace std;
    4. class TV;
    5. class YK{
    6. public:
    7. void up(TV& tv);
    8. void down(TV& tv);
    9. };
    10. class TV{
    11. friend class YK;
    12. private:
    13. int yl;
    14. public:
    15. TV(){}
    16. TV(int yl)
    17. {
    18. this->yl = yl;
    19. }
    20. };
    21. void YK::up(TV &tv)
    22. {
    23. tv.yl++;
    24. cout << "当前音量:" << tv.yl << endl;
    25. }
    26. void YK::down(TV &tv)
    27. {
    28. tv.yl--;
    29. cout << "当前音量:" << tv.yl << endl;
    30. }
    31. int main(int argc, char *argv[])
    32. {
    33. TV tv(10);
    34. YK yk;
    35. yk.up(tv);
    36. yk.up(tv);
    37. yk.up(tv);
    38. yk.down(tv);
    39. yk.down(tv);
    40. yk.down(tv);
    41. return 0;
    42. }

    string

    c++ 字符串类 , 使其字符串操作方便
    示例
    1. #include
    2. #include
    3. using namespace std;
    4. int main(int argc, char *argv[])
    5. {
    6. string str01 = "hello";
    7. string str02 = str01;//字符串赋值
    8. cout << str01 << endl;//字符串输出
    9. cout << str02 << endl;
    10. str02 = "world";
    11. cout << str01 << endl;
    12. cout << str02 << endl;
    13. string str03 = str01 + str02;//字符串拼接
    14. cout << str03 << endl;
    15. string str04;
    16. cin >> str04;//字符串输入
    17. cout << str04 << endl;
    18. string str05 = "Hi C++";
    19. string str06 = "Hi C++";
    20. string str07 = "Hi C";
    21. cout << (str05 == str06) << endl;//判断字符串内容是否相同
    22. cout << (str05 == str07) << endl;
    23. cout << &str05 << endl;//打印str05地址
    24. cout << &str06 << endl;//打印str06地址
    25. return 0;
    26. }

    重载

    引入

    经源码查看 string 发现其也是一个类
    那么为什么 string 的类对象可以使用 >>,<<,+,== 等运算符 , 我们自定义的类不行呢 ?
    因为 string 类对运算符进行了重载
    那我们如何实现运算符的重载

    概述

    作用

            是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

    关键字

            operator

    语法

            返回值类型 operator+ 运算符 ( 形参列表 )
            {
                    函数体
            }
            如:
            >>
            void operator>>(形参列表 )
            {
            }
    思路
    1 、分析运算符的运算对象的个数
    2 、分析运算符左边的运算对象是 自定对象 还是其他
    左边:是其他 只能全局函数实现 (必须使用友元)
    左边:自定义对象
    可以用使用全局函数重载运算符(参数个数 和 运算符对象的个数一致)
    也可以使用成员函数重载运算符(参数可以少一个) (推荐)

    示例1:重载<<,>>运算符

    效果
            使其可以通过<< 输出自定义类型的变量或通过 >> 输入自定义类型变量
    分析
             << >> 符号左边为 cout cin 不是自定义对象 , 只能使用全局函数对其进行重载
    示例
    1. #include
    2. #include
    3. using namespace std;
    4. class Data{
    5. public:
    6. int x,y,z;
    7. Data(){}
    8. Data(int a,int b,int c):x(a),y(b),z(c){}
    9. };
    10. //第一个参数为运算符左边的变量
    11. //第二个参数为运算符右边的变量
    12. istream& operator >>(istream& in,Data& d)
    13. {
    14. in >> d.x >> d.y >> d.z;
    15. return in;
    16. }
    17. ostream& operator <<(ostream& out,Data& d)
    18. {
    19. out << "x = " << d.x << "\ty = " << d.y << "\tz = " << d.z << endl;
    20. return out;
    21. }
    22. int main(int argc, char *argv[])
    23. {
    24. Data d;
    25. cin >> d;
    26. cout << d << endl;
    27. return 0;
    28. }

    示例2:重载+运算符

    效果

            使用+ 运算符将自定义类型对象的属性一一相加

    分析

            +符号左边为自定义类型 , 可以使用全局函数重载也可以使用成员函数中

    示例:全局函数重载

    1. #include
    2. using namespace std;
    3. class Data{
    4. public:
    5. int x,y,z;
    6. Data(){}
    7. Data(int a,int b,int c):x(a),y(b),z(c){}
    8. };
    9. //第一个参数为运算符左边的变量
    10. //第二个参数为运算符右边的变量
    11. Data* operator +(Data& d1,Data& d2)
    12. {
    13. Data *d = new Data();
    14. d->x = d1.x + d2.x;
    15. d->y = d1.y + d2.y;
    16. d->z = d1.z + d2.z;
    17. return d;
    18. }
    19. int main(int argc, char *argv[])
    20. {
    21. Data d1(1,2,3);
    22. Data d2(1,2,3);
    23. Data* d3 = d1 + d2;
    24. cout << d3->x << d3->y << d3->z << endl;
    25. return 0;
    26. }
    示例 2: 成员函数重载 + 运算符
    1. #include
    2. using namespace std;
    3. class Data{
    4. public:
    5. int x,y,z;
    6. Data(){}
    7. Data(int a,int b,int c):x(a),y(b),z(c){}
    8. //调用该函数的对象为运算符左边的变量
    9. //参数为运算符右边的变量
    10. Data* operator +(Data& d2)
    11. {
    12. Data *d = new Data();
    13. d->x = this->x + d2.x;
    14. d->y = this->y + d2.y;
    15. d->z = this->z + d2.z;
    16. return d;
    17. }
    18. };
    19. int main(int argc, char *argv[])
    20. {
    21. Data d1(1,2,3);
    22. Data d2(1,2,3);
    23. Data* d3 = d1 + d2;
    24. cout << d3->x << d3->y << d3->z << endl;
    25. return 0;
    26. }

    示例3:重载==运算符

    效果
            比较类中成员变量值是否相同
    分析
            符号左边为自定义类型, 可以使用全局函数重载也可以使用成员函数中
    示例
    1. #include
    2. using namespace std;
    3. class Data{
    4. public:
    5. int x,y,z;
    6. Data(){}
    7. Data(int a,int b,int c):x(a),y(b),z(c){}
    8. //调用该函数的对象为运算符左边的变量
    9. //参数为运算符右边的变量
    10. bool operator ==(Data& d2)
    11. {
    12. if(this->x == d2.x && this->y == d2.y && this->z == d2.z)
    13. {
    14. return true;
    15. }
    16. else
    17. {
    18. return false;
    19. }
    20. }
    21. };
    22. int main(int argc, char *argv[])
    23. {
    24. Data d1(1,2,3);
    25. Data d2(1,2,3);
    26. Data d3(2,2,3);
    27. cout << (d1 == d2) << endl;
    28. cout << (d1 == d3) << endl;
    29. return 0;
    30. }

    示例4:重载++运算符

    注意

    ++ 运算符分为 ++ 在前与 ++ 在后两种所以需要重载两种
    当编译器看到 ++a( 前置 ++), 它就调用 operator++(Type& a)( 全局函数 ),operator++
    ()( 成员函数 )
    当编译器看到 a++( 后置 ++), 它就会去调用 operator++(Type& a,int)( 全局函
    ),operator++(int)( 成员函数 )

    示例

    1. #include
    2. using namespace std;
    3. class Data{
    4. public:
    5. int x,y,z;
    6. Data(){}
    7. Data(int a,int b,int c):x(a),y(b),z(c){}
    8. Data& operator ++()//++前置
    9. {
    10. ++x;
    11. ++y;
    12. ++z;
    13. return *this;
    14. }
    15. Data operator ++(int)//++后置
    16. {
    17. Data old = *this;//记录旧值
    18. ++x;
    19. ++y;
    20. ++z;
    21. return old;//返回旧值
    22. }
    23. };
    24. ostream& operator <<(ostream& out,Data& d)
    25. {
    26. out << d.x << d.y << d.z << endl;
    27. return out;
    28. }
    29. int main(int argc, char *argv[])
    30. {
    31. Data d1(1,2,3);
    32. ++d1;
    33. cout << d1;
    34. Data d2(1,2,3);
    35. Data d3 = d2++;
    36. cout << d3;
    37. cout << d2;
    38. return 0;
    39. }

    示例5:重载*->

    要求

            重载指针运算符实现智能指针

    推演

    1. #include
    2. using namespace std;
    3. class Data{
    4. private:
    5. int x,y,z;
    6. public:
    7. Data(){
    8. cout << "无参构造函数" << endl;
    9. }
    10. Data(int a,int b,int c):x(a),y(b),z(c){
    11. cout << "有参构造函数" << endl;
    12. }
    13. ~Data()
    14. {
    15. cout << "析构函数" << endl;
    16. }
    17. };
    18. int main(int argc, char *argv[])
    19. {
    20. Data *p = new Data();
    21. return 0;
    22. }
    观察以上代码 , 我们发现创建的对象没有被销毁 , 但是我们在编写代码时经常会忘记销
    , 那该怎么办呢 ?
    解决方案如下
    1. #include
    2. using namespace std;
    3. class Data{
    4. private:
    5. int x,y,z;
    6. public:
    7. Data(){
    8. cout << "无参构造函数" << endl;
    9. }
    10. Data(int a,int b,int c):x(a),y(b),z(c){
    11. cout << "有参构造函数" << endl;
    12. }
    13. ~Data()
    14. {
    15. cout << "析构函数" << endl;
    16. }
    17. };
    18. class FreeData{
    19. private:
    20. Data* p;
    21. public:
    22. FreeData(){
    23. p = NULL;
    24. }
    25. FreeData(Data* data){
    26. p = data;
    27. }
    28. ~FreeData(){
    29. if(p != NULL)
    30. {
    31. delete p;
    32. p = NULL;
    33. }
    34. }
    35. };
    36. int main(int argc, char *argv[])
    37. {
    38. FreeData fd(new Data(1,2,3));
    39. return 0;
    40. }
    现在我们发现 Data 对象可以销毁 , 但是如何调用其对象中的属性呢 ?
    方案如下
    1. #include
    2. using namespace std;
    3. class Data{
    4. private:
    5. int x,y,z;
    6. public:
    7. Data(){
    8. cout << "无参构造函数" << endl;
    9. }
    10. Data(int a,int b,int c):x(a),y(b),z(c){
    11. cout << "有参构造函数" << endl;
    12. }
    13. ~Data()
    14. {
    15. cout << "析构函数" << endl;
    16. }
    17. int getX()
    18. {
    19. return x;
    20. }
    21. };
    22. class FreeData{
    23. private:
    24. Data* p;
    25. public:
    26. FreeData(){
    27. p = NULL;
    28. }
    29. FreeData(Data* data){
    30. p = data;
    31. }
    32. ~FreeData(){
    33. if(p != NULL)
    34. {
    35. delete p;
    36. p = NULL;
    37. }
    38. }
    39. Data& operator *()
    40. {
    41. return *p;
    42. }
    43. Data* operator ->()
    44. {
    45. return p;
    46. }
    47. };
    48. int main(int argc, char *argv[])
    49. {
    50. FreeData fd(new Data(1,2,3));
    51. cout << (*fd).getX() << endl;
    52. cout << fd->getX() << endl;
    53. return 0;
    54. }

    示例6:重载()

    作用

    当类对象作为函数调用时,会执行 operator()( 参数列表 ) 函数。
    对象作为函数调用
    对象名 ( 实参列表 );
    一种仿函数

    示例

    1. #include
    2. using namespace std;
    3. class Data{
    4. friend ostream& operator <<(ostream& out,Data& d);
    5. private:
    6. int x,y,z;
    7. public:
    8. Data(){
    9. cout << "无参构造函数" << endl;
    10. }
    11. Data(int a,int b,int c):x(a),y(b),z(c){
    12. cout << "有参构造函数" << endl;
    13. }
    14. ~Data()
    15. {
    16. cout << "析构函数" << endl;
    17. }
    18. void operator ()(int a,int b,int c){
    19. this->x += a;
    20. this->y += b;
    21. this->z += c;
    22. }
    23. };
    24. ostream& operator <<(ostream& out,Data& d)
    25. {
    26. out << d.x << "\t" << d.y << "\t" << d.z << endl;
    27. return out;
    28. }
    29. int main(int argc, char *argv[])
    30. {
    31. Data d(1,2,3);
    32. d(2,5,8);
    33. cout << d;
    34. return 0;
    35. }

    示例7:重载=

    注意
    = 重载时,可能会调用类本身的拷贝构造函数。
    如果左值是没有创建的对象时 , 会调用拷贝构造函数 .
    如果左值是已创建的类对象 , 会执行 = 重载函数 , 实现数据的拷贝
    示例
    1. #include
    2. using namespace std;
    3. class Data{
    4. friend ostream& operator <<(ostream& out,Data& d);
    5. private:
    6. int x,y,z;
    7. public:
    8. Data(){
    9. cout << "无参构造函数" << endl;
    10. }
    11. Data(int a,int b,int c):x(a),y(b),z(c){
    12. cout << "有参构造函数" << endl;
    13. }
    14. Data(const Data& d)
    15. {
    16. cout << "执行拷贝构造" << endl;
    17. this->x = d.x;
    18. this->y = d.y;
    19. this->z = d.z;
    20. }
    21. ~Data()
    22. {
    23. cout << "析构函数" << endl;
    24. }
    25. void operator =(Data& d){
    26. cout << "执行重载=运算符的函数" << endl;
    27. this->x = d.x;
    28. this->y = d.y;
    29. this->z = d.z;
    30. }
    31. };
    32. ostream& operator <<(ostream& out,Data& d)
    33. {
    34. out << d.x << "\t" << d.y << "\t" << d.z << endl;
    35. return out;
    36. }
    37. int main(int argc, char *argv[])
    38. {
    39. Data d1(1,2,3);
    40. Data d2(3,6,9);
    41. d1 = d2;//d1已完成初始化,执行重载的=号运算符
    42. Data d3 = d2;//d3未完成初始化,执行拷贝构造
    43. return 0;
    44. }

    注意:

  • 相关阅读:
    Go-知识sync map
    LeetCode(力扣)62. 不同路径Python
    Kinsoku jikou desu新浪股票接口变动(php)
    【网络原理】- 网络层 , 数据链路层的简单介绍
    mysql中 COALESCE和CASE WHEN的使用以及创建或替换视图
    MAC | linux | SSH 密钥验证
    GPT-4V:AI在教育领域的应用
    PLC-Recorder离线分析软件Ana里为什么不能显示变量的编号?
    python基础语法(JSON、类、对象)
    Cesium-移动实体
  • 原文地址:https://blog.csdn.net/aisheisadas/article/details/134562038