C++知识点的回顾(持续更新)-创新互联

目录
  • 一.数据的输入
  • 二.数据的输出
  • 三.内存分区模型
  • 四.引用
  • 五.函数(区别于C语言)
  • 六.类和对象

从事服务器托管,服务器租用,云主机,网页空间,域名与空间,CDN,网络代维等服务。一.数据的输入
  • 语法:cin>>变量
二.数据的输出
  • 语法:cout<<输出项<
    • endl相当于
    • 输出项可以是字符串,也可以是变量
三.内存分区模型
  1. 意义
    • 对不同区域存放的数据赋予不同的生命周期
  2. 分区
    • 代码区:存放函数体的二进制代码,由操作系统进行管理

      • 程序执行前出现
      • 存放CPU执行的机器指令
      • 代码区数据共享(不用反复生成)
      • 代码区数据只读(不可修改)
    • 全局区:存放全局变量和静态变量以及常量(const修饰的全局常量和字符串常量)

      • 程序执行前出现
      • 存储全局变量和静态变量和常量
      • 程序执行 完后由操作系统释放
    • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量,局部常量,形参数据等

      • 注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
        • 如:

          #includeusing namespace std;
          
          int* func()
          {int a = 10;
          	return &a;//返回局部变量的地址
          }
          
          int main()
          {int* p = func();
          	cout<< *p<< endl;//#10    第一次打印正确数字因为编译器替你保留了一次
          	cout<< *p<< endl;//#???第二次这个数据就不保留了(visual studio2022编译器会一直保留)
          
          	system("pause");
          
          	return 0;
          }
    • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

      • 在c++中主要利用new在堆区开辟内存
        • 语法:int *p=new int(10)int *arr=new int[10]
          • new int(10)返回的是开辟的内存的首地址,所以用指针去接收
          • (10)表示初始值为10
          • [10]表示开辟10个元素
      • 在c++中主要利用delete在堆区释放内存
        • 语法:delete pdelete[] arr
          • p是之前开辟的内存地址,直接delete内存地址即可释放
          • 释放数组空间需要加[]
四.引用
  1. 作用:给变量取别名,共用同一个内存空间
  2. 语法:数据类型 &别名=原名
  3. 注意事项
    • 引用必须初始化
      • 错误:
        int &b;
    • 引用在初始化后不可以改成别的别名
      • 错误:
        int &b=a;
        int &b=c;//更改引用成别的变量的别名会报错
  4. 引用形参
    • 作用:用形参修饰实参
    • 有点:比指针方便
    • 函数参数传递对比
      1. 值传递(形参不修饰实参)

        //1.值传递
        void val_swap(int a, int b)
        {int temp = a;
        	a = b;
        	b = temp;
        }
        
        int main()
        {int a = 10;
        	int b = 20;
        	val_swap(a, b);
        	cout<< a<< " "<< b<< endl;
        
        	system("pause");
        	return 0;
        }

        在这里插入图片描述

      2. 地址传递(形参修饰实参)

        //2.地址传递
        void address_swap(int *a, int *b)
        {int temp = *a;
        	*a = *b;
        	*b = temp;
        }
        
        int main()
        {int a = 10;
        	int b = 20;
        	val_swap(&a, &b);
        	cout<< a<< " "<< b<< endl;
        
        	system("pause");
        	return 0;
        }

        在这里插入图片描述

      3. 引用传递(形参修饰实参)

        //3.引用传递
        void quote_swap(int &a, int &b)
        {int temp = a;
        	a = b;
        	b = temp;
        }
        
        int main()
        {int a = 10;
        	int b = 20;
        	quote_swap(a, b);
        	cout<< a<< " "<< b<< endl;
        
        	system("pause");
        	return 0;
        }

        在这里插入图片描述

  5. 引用做函数返回值
    • 作用:作为函数返回值

    • 注意:不要返回局部变量的引用

    • 代码:

      int& test1()
      {int a = 10;
      	return a;
      }
      
      int main()
      {int &ref = test1();
      	cout<< ref<< endl;//10
      	cout<< ref<< endl;//???(但是visual studio 2022编译器永久保留了内存,所以也是10)
      
      	system("pause");
      
      	return 0;
      }
      int& test2()
      {static int a = 10;//堆区的静态变量
      	return a;
      }
      
      int main()
      {int &ref1 = test2();
      	cout<< ref1<< endl;//10
      	cout<< ref1<< endl;//10
      
      	test2() = 1000;//左值(作为等号左边)
      	cout<< test2()<< endl;//1000 a的别名是test2()
      	cout<< ref1<< endl;//1000 a的别名是ref1
      
      	system("pause");
      	return 0;
      }
  6. 引用的本质
    • 本质:指针常量(指针的指向不可以修改,指针指向的值是可以改动的)(引用相当于加了一个限制(不可以指向别的地方)的指针)(转换出来的const修饰的是变量名,即地址,所以指向不可以修改)

    • 代码:

      //引用的本质(编译器自动将引用转化成指针常量形式)
      void func(int "e)//->void func(int *const quote)
      {quote = 30;//->*quote=30
      }
      int main()
      {int a = 10;
      	int "e = a;//->int *const quote=&a
      	quote = 20;//->*quote=20
      	cout<< a<< endl;
      	cout<< quote<< endl;//->cout<<*quote<func(&a)
      	cout<< a<< endl;
      	cout<< quote<< endl;//->cout<<*quote<
  7. 常量引用
    • 本质:(指针的指向不可以修改,指针指向的值是也不可以修改)

    • 作用:常量引用主要用来修饰形参,防止形参改变实参

      //常量引用
      void func(const int& ref)
      {//无法通过修改ref的值去改变main中的值,防止误操作
      	//ref = 1000;
      	cout<< ref<< endl;
      }
      
      int main()
      {//引用本身需要一个合法的内存空间,所以下面注释掉的这行是错误的,得用下下行
      	//int& ref = 10;
      	const int &ref = 10;//->const int temp=10;const int &ref=temp;
      
      	int a = 10;
      	func(a);
      	system("pause");
      	return 0;
      }
五.函数(区别于C语言)
  1. 函数默认参数
    • 语法:返回值类型 函数名(参数1,参数2=默认值,参数3=默认值,···){}

    • 注意:

      1. 如果形参列表中某个位置有了默认值,那么这个位置之后所有位置都要先设置默认值
      2. 如果函数声明有默认参数,函数实现中就不能写默认参数,否则会出现二义性
    • 代码:

      //函数默认参数
      int func(int a, int b = 10, int c = 20)
      {return a + b + c;
      }
      
      int main()
      {cout<< func(10,30)<< endl;
      
      	system("pause");
      	return 0;
      }
  2. 函数占位参数(占位参数也可以有默认参数)
    • 语法:返回值类型 函数名(数据类型){}返回值类型 函数名(数据类型 = 值){}
    • 注意:调用带占位参数的函数需要填补占位参数
    • 代码:
      //函数占位参数
      void func(int a, int)
      {cout<< "不知道有啥用"<< endl;
      }
      
      int main()
      {func(10, 20);
      
      	system("pause");
      	return 0;
      }
  3. 函数重载
    • 作用:函数名可以相同,提高复用性
    • 函数重载满足条件:
      • 同一个作用域下
      • 函数名称相同
      • 函数参数类型不同或者参数个数不同或者参数顺序不同
    • 注意:
      1. 函数的返回值不可以进行函数重载
      2. 形参名字不同不可以进行函数重载
        • 代码:
          void func()
          {cout<< "func()的调用"<< endl;
          }
          
          //参数类型不同
          void func(int a)
          {cout<< "func(int a)的调用"<< endl;
          }
          
          void func(double a)
          {cout<< "func(double a)的调用"<cout<< "func(int a, double b)的调用"<< endl;
          }
          
          //参数顺序不同
          void func(double a, int b)
          {cout<< "func(double a, int b)的调用"<< endl;
          }
          
          int main()
          {func();//第一个函数的调用
          	func(10);//第二个函数的调用
          	func(10.0);//第三个函数的调用
          	func(10, 10.0);//第四个函数的调用
          	func(10.0, 10);//第五个函数的调用
          }
      3. 引用作为重载条件
        • 代码:

          //1.引用作为重载的条件
          void func(int &a)
          {cout<< "func(int &a)调用"<< endl;
          }
          
          void func(const int &a)//引用常量
          {cout<< "func(const int &a)调用"<< endl;
          }
          
          int main()
          {int a = 0;
          	func(a);//第一个函数被调用
          	func(0);//第二个函数被调用
          
          	system("pause");
          	return 0;
          }
      4. 函数重载碰到函数默认参数(写函数重载时尽量不要有默认参数,否则容易出现如下的二义性)
        • 代码:

          //2.函数重载碰到默认参数
          void func(int a,int b=10)
          {cout<< "func(int a,int b=10)调用"<< endl;
          }
          
          void func(int a)//引用常量
          {cout<< "func(int a)调用"<< endl;
          }
          
          int main()
          {//func(10);//这样是调用不了的
          	func(10, 20);//这样才能调用
          
          
          	system("pause");
          	return 0;
          }
六.类和对象
  1. C++面向对象特性:封装、继承、多态

  2. 类和结构体的唯一区别

    • struct的默认权限为公共,class默认权限为私有
  3. 封装

    • 封装的意义
      • 将属性和行为作为一个整体,表现生活中的事物
        • 语法:
          class 类名 
          {访问权限
          	属性
          	行为
          };
          类名 变量名;
      • 将属性和行为加以权限控制
        • 访问权限

          权限语法特点继承方面特点
          公共权限public类内可以访问,类外可以访问
          保护权限protected类内可以访问,类外不可以访问子类继承父类后可以访问
          私有权限private类内可以访问,类外不可以访问子类继承父类后还是不可以访问
    • 一般通过访问公共成员函数去对私有成员属性进行读写操作
      • 优点
        • 自己控制读写权限
        • 检测用户输入数据是否有效
      • 代码:
        class Person
        {public:
        	//可读可写
        	void setName(string name)
        	{m_Name = name;
        	}
        	string getName()
        	{return m_Name;
        	}
        	//只读
        	int getAge()
        	{m_Age = 0;
        		return m_Age;
        	}
        
        	//只写
        	void setFriend(string Friend)
        	{m_friend = Friend;
        	}
        
        
        private:
        	string m_Name;
        	int m_Age;
        	string m_friend;
        };
        
        int main()
        {Person p;
        
        	p.setName("张三");
        	cout<< p.getName()<< endl;
        
        	cout<< p.getAge()<< endl;
        
        	p.setFriend("friend");
        
        	
        
        	system("pause");
        	return 0;
        }
  4. 对象特性

    1. 对象的初始化和清理

      1. 构造函数(自动调用)
        • 作用:创建对象时为对象的成员属性赋值
        • 语法:类名(){};
        • 特点:
          1. 无返回值,也不写void
          2. 构造函数的函数名就是该类的类名
          3. 构造函数可以有形参,可发生重载
          4. 在调用对象时系统自动调用且只调用一次
          5. 需要注意构造函数也有访问权限,如果不声明公共权限,类外创建对象会无法自动调用构造函数,会报错
        • 分类:
          1. 按参数分类:
            1. 有参构造
              • 语法:类名(数据类型 变量名){};
            2. 无参构造(默认构造)
              • 语法:类名(){};
          2. 按类型分类:
            1. 普通构造
            2. 拷贝构造
              • 调用时机(黑马P108,没听懂,太难了)
                1. 使用一个已经创建完毕的对象来初始化一个新对象
                2. 值传递的方式给函数参数传值
                3. 以值方式返回局部对象
                  • 返回的是一个副本对象,与原本对象不在同一个内存空间
              • 深拷贝与浅拷贝(如果成员属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题)
                • 浅拷贝:简单的赋值拷贝操作(使用指针时先拷贝会指向同一片内存空间,前后两个指针指向同一块内存空间)

                • 深拷贝:在堆区重新申请空间,进行拷贝操作(在自己写的拷贝构造函数中重新申请空间)(使用指针时深拷贝会指向别的地方的内存空间,前后两个指针之间没有交集)

                • 代码:

                  #includeusing namespace std;
                  
                  
                  class Person
                  {public:
                  	Person()
                  	{cout<< "默认构造"<< endl;
                  	}
                  	Person(int age,int height)
                  	{m_Age = age;
                  		m_Height = new int(height);//括号内放的是元素
                  		cout<< "有参构造"<< endl;
                  	}
                  	Person(const Person& p)
                  	{cout<< "深拷贝构造"<< endl;
                  		m_Age = p.m_Age;
                  		//m_Height = p.m_Height;//这是编译器默认实现的代码,是浅拷贝
                  		m_Height = new int(*p.m_Height);//深拷贝
                  	}
                  	~Person()
                  	{if (m_Height != NULL)
                  		{	delete m_Height;
                  			m_Height = NULL;
                  		}
                  
                  	}
                  	int m_Age;
                  	int *m_Height;
                  };
                  
                  void  test1()
                  {Person p1(10,160);
                  	cout<< p1.m_Age<<*p1.m_Height<< endl;
                  	Person p2(p1);
                  	cout<< p2.m_Age<< *p2.m_Height<test1();
                  
                  	system("pause");
                  	return 0;
                  }
        • 调用方式
          1. 括号法
            • 注意:调用默认构造函数时不要加(),即不要类名 变量名();,因为编译器会认为i这是函数的声明;

            • 代码:

              //1.括号法
              	Person p1(10);//使用有参构造
              	Person p2(p1);//使用拷贝构造
              	cout<< p1.age<< p2.age<
          2. 显式法
            • 注意:

              • 类名(实参);是匿名对象,当前行执行结束后,系统会立即回收掉匿名对象

              • 不要利用拷贝构造函数初始化匿名对象,编译器会自动去掉括号认为在定义,就会出现重定义报错

              • 代码:

                Person p1 = Person(10);//使用有参构造
                	Person p2 = Person(p1);//使用拷贝构造
                
                	Person(10);//匿名对象 对
                	Person(p1);//错
                	Person(p2);//错
            • 代码:

              //2.显示法
              	Person p1 = Person(10);//使用有参构造
              	Person p2 = Person(p1);//使用拷贝构造
          3. 隐式转换法
            • 代码:

              //3.隐式转换法
              	Person p4 = 10;//->Person p4=Person(10)//使用有参构造
              	Person p5 = p4;//->Person p5=Person(p4)//使用拷贝构造
      2. 析构函数(自动调用)
        • 作用:在对象销毁前系统自动调用执行请理工作(将堆区开辟数据做释放)
        • 语法:~类名(){}
        • 特点:
          1. 无返回值,也不写void
          2. 析构函数的函数名就是~+该类的类名
          3. 析构函数不可以有形参,所以不可以发生重载
          4. 在销毁对象前系统自动调用且只调用一次
      • 代码:

        #includeusing namespace std;
        
        class Person
        {public:
        	//1.构造函数
        	Person()
        	{cout<<"Person构造函数调用"<< endl;
        	}
        
        	//2.析构函数
        	~Person()
        	{cout<< "Person析构函数调用"<< endl;
        	}
        };
        
        
        int main()
        {Person person;//创建对象后就出现构造调用
        	system("pause");//按任意键后出现析构调用,因为main函数执行完后销毁了对象
        	return 0;
        }
      • 调用规则:

        • 首先默认情况下编译器至少给一个类添加3个函数:
          1. 默认构造函数(无参,函数体为空)
          2. 默认析构函数(无参,函数体为空)
          3. 默认拷贝构造函数,对属性进行值拷贝(参数为:const 类名 &变量名
        1. 如果用户定义有参构造函数,c++不再提供默认的无参构造,但是会提供默认拷贝(即用户只提供有参,编译器不会提供无参,但提供拷贝)
          • 因此如果你只写有参构造,但不写默认构造,然后去调用默认构造编译器会报错
        2. 如果用户定义拷贝构造函数,c++不会再提供其他构造函数(即用户只提供拷贝,编译器不会提供无参,也不会提供有参)
          • 因此如果你只写拷贝构造,但不写默认构造和有参构造,然后去调用默认构造或有参构造编译器会报错
    2. 初始化列表

      • 语法:构造函数类名():属性1(值1或变量1),属性2(值2或变量2){}

      • 代码:

        #includeusing namespace std;
        
        class Person
        {public:
        
        	Person(int a,int b,int c):m_A(a),m_B(b),m_C(c)
        	{;
        	}
        
        	int m_A;
        	int m_B;
        	int m_C;
        };
        int main()
        {Person p(10,20,30);
        	cout<< p.m_A<< p.m_B<< p.m_C<< endl;
        	system("pause");
        	return 0;
        }
    3. 类对象作为类成员

      • 注意:

        1. 构造顺序:当其他类对象作为本类成员,构造时先构造类对象,再构造自身(先里再外)
        2. 析构顺序:当其他类对象作为本类成员,析构时先析构自身,再析构类对象(先外再里)
      • 代码

        #includeusing namespace std;
        
        //类对象作为类成员
        class Phone
        {public:
        	Phone()
        	{m_PhoneNum = 12345678901;
        	}
        	long long int m_PhoneNum;
        };
        
        class Person
        {public:
        	Phone m_Phone;
        };
        
        int main()
        {Person p;
        	cout<< p.m_Phone.m_PhoneNum<< endl;//12345678901
        	system("pause");
        	return 0;
        }
    4. 静态成员(静态可以理解为共用)

      • 定义:在成员变量和成员函数前加上关键字static
      • 语法:static a;
      • 分类:
        1. 静态成员变量(全局区)
          • 所有对象共享同一份数据
          • 在编译阶段分配内存
          • 类内声明,类外初始化(类外定义)
            • 类内:static 变量类型 变量名;
            • 类外:变量类型 类名::变量名;
              • 表示定义类名作用域下变量名这个静态成员
          • 访问方式:
            1. 通过对象进行访问
              • 语法:类名 变量名;变量名.静态成员变量;
            2. 通过类名进行访问(类名作用域)
              • 语法:类名::静态成员变量;
        2. 静态成员函数
          • 所有对象共享同一个函数
          • 静态成员函数只能访问静态成员变量,不可以访问非静态成员变量
            • 因为当使用访问方式2去访问时,不知道改变的非静态成员变量是属于哪个对象的
          • 访问方式:
            1. 通过对象进行访问
              • 语法:类名 变量名;变量名.静态成员函数;
            2. 通过类名进行访问(类名作用域)
              • 语法:类名::静态成员函数;
      • 区别:
        • 静态成员函数不需要初始化(定义),和静态成员变量不同
    5. C++对象模型和this指针

      1. 成员变量和成员函数分开存储

        • 空对象占用内存空间为1

          • C++编译器会给每个空对象分配一个字节空间,是为了区分各个空对象占内存的位置,将它们分隔开(每一个空对象都应该有一个独一无二的空间)
        • 除了非静态成员变量属于类的对象,占用正常变量类型的字节,其余(静态成员变量,非静态成员函数,静态成员函数)都不属于类的对象,不占用字节

        • 代码

          class Person
          {public:
          	int m_A;//非静态成员变量,属于类的对象上//即属于p//占4字节
          	static int m_B;//静态成员变量,不属于类的对象上//即不属于p//所以占用字节为0
          	void func1() {};//非静态成员函数,也是不属于类的对象上//即不属于p//所以占用字节为0//比较特殊
          	static void func2(){}//静态成员半数,不属于类的对象上//即不属于p//所以占用字节为0
          };
      2. this指针

        • 本质:指针常量(this指针指向不可以修改,this指针指向的值是可以修改的)

        • 概念:this指针指向被调用的成员函数所属的对象(哪个对象调用了成员函数(得先调用成员函数,才能让this指针指向),this指针就指向谁)

        • 用途

          • 当形参和成员变量同名时,可以用this指针区分
          • 在类的非静态成员函数中返回对象本身,可以用return *this
            • 即this指针指向对象,解引用this即可得到对象
        • 代码:

          #includeusing namespace std;
          
          class Person
          {public:
          	Person(int m_Age)
          	{this->m_Age=m_Age;//this 指向调用了该成员函数的对象p,所以使用p->age去接收形参m_Age
          	}
          
          	//返回值用引用返回的就是对象本体,指向本身内存,如果不用引用返回的是对象副本,与对象没有关系,后续的操作就改变不了对象
          	Person &PersonAdd(Person& p)//不懂这里的返回值为什么是Person &,不能是Person或Person *
          	{this->m_Age += p.m_Age;
          		//this是指向调用了这个函数的对象,在这里是p3
          		return *this;
          	}
          
          	int m_Age;
          };
          
          //1.解决名称冲突
          void test1()
          {Person p1(10);
          	cout<< p1.m_Age<< endl;//10
          }
          
          //2.返回对象本身用*this 
          void test2()
          {Person p2(1);
          	Person p3(2);
          	//链式编程思想//cout<<<<也是链式编程思想
          	p3.PersonAdd(p2).PersonAdd(p2);//2+1+1//如果使用Person PersonAdd(p2)则这个地方会调用p3的默认拷贝构造函数,返回的是一个新副本,与p3不在同一个内存空间
          	cout<< p3.m_Age<< endl;//4
          }
          
          int main()
          {test1();
          	test2();
          	system("pause");
          	return 0;
          }
      3. 空指针访问成员函数

        • 注意:有没有用到this指针,如果用来this指针就要加判断保证代码的健壮性

        • 代码:

          #includeusing namespace std;
          
          //空指针调用成员函数
          
          class Person
          {public:
          	void showName()
          	{cout<< "Name"<< endl;
          	}
          	void showAge()
          	{if(this==NULL)//不懂这里为什么不是*this==NULL
          		{	return;
          		}
          		cout<< m_Age<< endl;//这个报错原因是因为默认将m_Age变成this->m_Age,但此时this指向是NULL,所以报错
          	}
          	int m_Age = 10;
          };
          
          void test1()
          {Person *p=NULL;//空指针
          	p->showName();
          	p->showAge();
          
          }
          
          int main()
          {test1();
          	system("pause");
          	return 0;
          }
      4. const修饰成员函数

        • 常函数:

          • 成员函数后加const为常函数
            语法:函数返回类型 函数名() const{}
          • 常函数内不可以修改成员属性
            • 因为this指针(类名 *const this)(指针常量)变成了(const 类名 *const this)(常指针常量)(此时指针指向的值也不可以修改了)
          • 成员属性(成员变量)声明时加关键字mutable后,在常函数中依然可以修改
        • 常对象(不能通过对象.成员变量的方式修改成员变量值,除非成员变量声明时加关键字mutable):

          • 声明对象前加const称该对象为常对象
          • 常对象只能调用常函数
        • 代码:

          #includeusing namespace std;
          
          //常函数
          class Person
          {public:
          	void showPerson() const
          	{//this->m_A = 100;//让指针指向的值也不可以改变
          		this->m_B = 100;//成员变量加mutable关键字后可以修改
          	}
          
          	void func()
          	{}
          
          	int m_A;
          	mutable int m_B;//特殊变量,加上关键字mutable后,即使在常函数中,也可以修改这个值
          };
          
          
          
          int main()
          {const Person p2;//常对象
          	//p2.m_A=10;//报错//常对象不能修改成员属性值,除非成员变量声明时前面加mutable
          	p2.m_B = 10;
          
          	//常对象只能调用常函数
          	p2.showPerson();
          	//p2.func();//报错//常对象不可以调用普通成员函数,因为普通成员函数可以修改成员属性值,而常对象定义就是不能修改成员属性值
          
          	system("pause");
          	return 0;
          }
  5. 友元

    • 关键字:friend
    • 目的:让类外特殊的一些函数或者类访问改类中的私有成员
    • 友元的三种实现:
      1. 全局函数做友元
        • 语法:在类中写friend 函数声明即可在类外使用该函数对类中的私有属性进行访问

        • 代码:

          #include#includeusing namespace std;
          
          
          class Building
          {//friend 加上这一句函数声明即可在类外使用该函数去访问私有属性
          	friend void goodGay(Building &building);
          public:
          	Building()
          	{m_SittingRoom = "客厅";
          		m_BedRoom = "卧室";
          	}
          
          	string m_SittingRoom;//客厅
          private:
          	string m_BedRoom;//卧室
          };
          
          //全局函数
          void goodGay(Building &building)
          {cout<< building.m_SittingRoom<< endl;
          	cout<< building.m_BedRoom<< endl;
          }
          
          void test1()
          {Building b;
          	goodGay(b);
          }
          int main()
          {test1();
          
          	system("pause");
          	return 0;
          }
      2. 类做友元
        • 语法:在类1中写friend 类2声明即可使用类2中的所有成员函数去访问类1的私有属性

        • 代码:

          #include#includeusing namespace std;
          
          //类做友元
          
          class Building;//类声明
          class GoodGay
          {public:
          	GoodGay();//先在类内声明
          	void visit();//先在类内声明
          	Building *b;
          };
          
          class Building
          {friend class GoodGay;//友元+类声明后可以使用该类去访问给出友元的类的私有属性
          public:
          	Building();//先在类内声明
          	string m_SittingRoom;
          private:
          	string m_BedRoom;
          };
          
          //再在类外写成员函数,需要声明在该类域下
          GoodGay::GoodGay()
          {b = new Building;
          }
          
          void GoodGay::visit()
          {cout<< b->m_SittingRoom<< endl;
          	cout<< b->m_BedRoom<< endl;//使用友元访问私有属性
          }
          
          
          //再在类外写成员函数,需要声明在该类域下
          Building::Building()
          {m_SittingRoom = "客厅";
          	m_BedRoom = "卧室";
          };
          
          void test1()
          {GoodGay g;
          	g.visit();
          }
          
          int main()
          {test1();
          	system("pause");
          	return 0;
          }
      3. 成员函数做友元
        • 语法:在类1中写friend 成员函数返回类型 类2名字::成员函数名();即可使用类2中的该成员函数去访问类1的私有属性

        • 代码:

          #include#includeusing namespace std;
          
          class Building;
          class GoodGay
          {public:
          	GoodGay();
          
          	void visit();//让visit函数可以访问Building中私有成员
          	Building *b;
          };
          
          
          class Building
          {friend void GoodGay::visit();//GoodGay类下的visit函数作为Building类的友元可以访问Building类的私有属性
          public:
          	Building();
          	string m_SittingRoom;
          private:
          	string m_BedRoom;
          };
          Building::Building()
          {m_SittingRoom = "客厅";
          	m_BedRoom = "卧室";
          }
          
          GoodGay::GoodGay()
          {b = new Building;
          }
          
          void GoodGay::visit()
          {cout<< b->m_SittingRoom<< endl;
          	cout<< b->m_BedRoom<< endl;
          }
          
          void test()
          {GoodGay g;
          	g.visit();
          }
          int main()
          {test();
          	system("pause");
          	return 0;
          }
  6. 类外写成员函数

    • 代码:

      class Building;
      class GoodGay
      {public:
      	GoodGay();//先在类内声明
      	void visit();//先在类内声明
      	Building *b;
      };
      
      class Building
      {public:
      	Building();//先在类内声明
      	string m_SittingRoom;
      private:
      	string m_BedRoom;
      };
      
      //再在类外写成员函数,需要声明在该类域下
      GoodGay::GoodGay()
      {b = new Building;
      }
      
      void GoodGay::visit()
      {cout<< b->m_SittingRoom<< endl;
      }
      
      
      //再在类外写成员函数,需要声明在该类域下
      Building::Building()
      {m_SittingRoom = "客厅";
      	m_BedRoom = "卧室";
      };
  7. 运算符重载

    • 运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
    • 注意:
      1. 编译器会自动简化运算符
      2. 运算符重载也可以发生函数重载
      3. 对于内置的数据类型的表达式的运算符是不可能改变的,比如(1+1=2,float,int等数据类型),自定义的数据类型才可以改变
      4. 不要滥用运算符重载
    • 常用:成员函数和全局函数进行运算符重载
    • 分类:
      1. 加号运算符重载
        • 关键:operator<<

        • 作用:实现两个自定义数据类型相加的运算

        • 代码:

          #includeusing namespace std;
          
          //加号运算符重载
          
          //1.成员函数重载+号
          class Person1
          {public:
          	Person1()
          	{m_A = 10;
          		m_B = 20;
          	}
          	Person1 operator+(Person1& p)
          	{Person1 temp;
          		temp.m_A = this->m_A + p.m_A;
          		temp.m_B = this->m_B + p.m_B;
          		return temp;
          	}
          
          	int m_A;
          	int m_B;
          };
          void test1()
          {Person1 p1;
          	Person1 p2;
          	Person1 p3 = p1+p2;//被编译器简化了,原本应该写成Person1 p3 = p1.operator+(p2);
          	cout<< p3.m_A<< " "<< p3.m_B<< endl;//20 40
          }
          
          
          
          //2.全局函数重载+号
          class Person2
          {public:
          	Person2()
          	{m_A = 10;
          		m_B = 20;
          	}
          	int m_A;
          	int m_B;
          };
          
          Person2 operator+(Person2 &p1, Person2 &p2)
          {Person2 temp;
          	temp.m_A = p1.m_A + p2.m_A;
          	temp.m_B = p1.m_B + p2.m_B;
          	return temp;
          }
          
          void test2()
          {Person2 p1;
          	Person2 p2;
          	Person2 p3 = p1 + p2;//被编译器简化了,原本应该写成Person2 p3 = operator+(p1,p2);
          	cout<< p3.m_A<< " "<< p3.m_B<< endl;//20 40
          }
          
          //3.全局函数使用函数重载+号
          class Person3
          {public:
          	Person3()
          	{m_A = 10;
          		m_B = 20;
          	}
          	int m_A;
          	int m_B;
          };
          
          Person3 operator+(Person3& p1, int num)
          {Person3 temp;
          	temp.m_A = p1.m_A + num;
          	temp.m_B = p1.m_B + num;
          	return temp;
          }
          
          void test3()
          {Person3 p1;
          	Person3 p3 = p1 + 100;//被编译器简化了,原本应该写成Person3 p3 = operator+(p1,100);
          	cout<< p3.m_A<< " "<< p3.m_B<< endl;//110 120
          }
          
          int main()
          {test1();
          	test2();
          	test3();
          	system("pause");
          	return 0;
          }
      2. 左移运算符重载
        • 关键:operator<<

        • 作用:自定义<<

        • 注意:

          1. 一般不用成员函数进行左移运算符重载,而是使用全局函数
          2. 重载左移运算符配合友元可以实现输出自定义数据类型
        • 代码:

          #includeusing namespace std;
          
          1.成员函数重载左移运算符
          //class Person1
          //{//public:
          //	Person1()
          //	{//		m_A = 10;
          //		m_B = 20;
          //	}
          //	//通常不会利用成员函数重载<<运算符,因为无法实现cout在左侧
          //	//p.operator<//
          //	//}
          //	int m_A;
          //	int m_B;
          //};
          //
          //void test1()
          //{//	Person1 p1;
          //	cout<< p1<< endl;
          //}
          
          
          //2.成员函数重载左移运算符
          class Person2
          {friend ostream& operator<<(ostream& cout, Person2& p2);//简化为 cout<

          m_A = 10; m_B = 20; } private: int m_A; int m_B; }; ostream &operator<<(ostream &cout, Person2 &p2)//简化为 cout<

          cout<< "m_A="<< p2.m_A<< "m_B="<< p2.m_B ; return cout; } void test2() {Person2 p2; cout<< p2<<"hi"<< endl;//可是这时用了endl后时传参给p2吗,好奇怪//因为这里发生了函数重载,endl或者"hi"的类型和Person2不同,所以不会调用自定义左移运算符,而是用系统自带的 } int main() {//test1(); test2(); system("pause"); return 0; }

      3. 递增运算符重载
        • 作用:通过重载递增运算符,实现自己的整型数据

        • 注意:

          1. 前置递增需要返回引用
          2. 后置递增不能返回引用(因为是局部对象,函数执行完后会被释放掉),要返回值
        • 代码:

          #includeusing namespace std;
          
          //重载递增运算符
          
          //自定义整形
          class MyInteger
          {friend ostream &operator<<(ostream& cout, MyInteger myint);
          public:
          	MyInteger()
          	{m_Num = 0;
          	}
          
          	//重载前置++运算符//不需要传参
          	MyInteger &operator++()//返回类型得用引用,不用引用的话只是副本//返回引用是为了一直对一个数据进行递增操作
          	{this->m_Num++;//先自身做加加运算
          
          
          		return *this;//再对自身做一个返回
          	}
          
          	//重载后置++运算符
          	MyInteger operator++(int)//int(也只能用int)代表占位参数,可以用于区分前置递增运算符和后置递增运算符//不能返回引用,因为函数执行完后temp会被释放掉,需要一个副本
          	{//先 记录此时的结果
          		MyInteger temp = *this;//temp是局部对象,不能返回引用,因为函数执行完后temp会被释放掉
          
          		//后 让自身的值++
          		this->m_Num++;
          
          		//最后将之前记录的结果返回
          		return temp;
          	}
          private:
          	int m_Num;
          };
          
          //重载<<运算符
          ostream &operator<<(ostream& cout, MyInteger myint)
          {cout<< myint.m_Num;
          	return cout;
          }
          
          void test1()
          {MyInteger myint;
          	cout<< ++myint<< endl;
          }
          
          void test2()
          {MyInteger myint;
          	cout<< (myint++)++<< endl;
          	cout<< myint<< endl;
          }
          
          int main()
          {//test1();
          	test2();
          	system("pause");
          	return 0;
          }
      4. 赋值运算符号重载
        • c++编译器至少给一个类加4个函数:
          1. 默认构造函数(无参,函数体为空)
          2. 默认析构函数(无参,函数体为空)
          3. 默认拷贝构造函数,对属性进行值拷贝
          4. 赋值运算符operator=,对属性进行值拷贝
        • 注意:
          1. 如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享题目:C++知识点的回顾(持续更新)-创新互联
网站URL:http://scyanting.com/article/gshej.html