zoukankan      html  css  js  c++  java
  • cpp

    4 类和对象

    4.2 对象的初始化和清理

    4.2.6 初始化列表

    • 作用: 初始化对象的属性
    • 为什么使用: 初始化发生在构造函数的语句之前, 若是使用构造函数内的语句赋值会浪费性能;
    • 语法: 构造函数():属性1(值1),属性2(值2)...{}
    class Person 
    {
        person(int a, int b, int c): m_a(a), m_b(b), m_c(c)
        {
            
        }
    };
    void test()
    {
        Person person(10, 20, 30);
    }
    

    4.2.7 类对象作为类成员

    c++中类的成员可以是另一个类的对象, 称为对象成员

    对象成员先构造后析构

    例如:

    class A{};
    class B
    {
        A a;
    };
    
    #include <iostream>
    using namespace std;
    #include <string>
    //手机类
    class Phone
    {
    public:
        Phone(String pName): m_PName(pName){}
        String m_PName;
    };
    class Person
    {
    public:
        Person(string name, String pName): m_Name(name), m_Phone(pName){}
        String m_Name;
        String m_Phone;
    };
    void test(){
        Person p("张三", "手机");
    }
    

    4.28 静态成员

    在成员变量和成员函数前加 static 关键字, 称为静态成员

    访问方法: 静态成员可以使用对象访问, 也可以使用类名访问

    静态成员也是有访问权限的

    • 静态成员变量
      • 多有对象共享同一份数据
      • 在编译阶段分布内存
      • 类内声明, 类外初始化
    • 静态成员函数
      • 所有对象共享一个函数
      • 静态成员函数只能访问静态成员变量
    class Person
    {
        public:
        static int m_A;
    };
    
    int Person::m_A = 100;
    void test()
    {
        Person p;
        //对象访问
        cout << p.m_A << endl;
        //类名访问
        cout << Person::m_A << endl;
    }
    

    4.3 c++对象模型和this指针

    4.3.1 成员变量和成员函数分开存储

    空对象的 sizeof 为 1, 若不是空的就是该多少多少

    只有非静态成员变量属于类的对象上

    4.3.2 this指针

    this指针指向被调用的成员函数所属的对象

    用途:

    1. 解决名称冲突

    2. 返回对象本身

      可以链式编程

      class Person
      {
          public:
          Person(int age):m_Age(age){}
          int m_Age;
          Person& PersonAddAge(Person &p)
          {
              m_Age += p.m_Age;
              return *this;
          }
      };
      void test()
      {
          Person p1(10):
          Person p2(10);
          p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
          cout << "p2.m_Age=" << p2.m_Age;
      }
      

    4.3.3 空指针访问成员函数

    空指针可以访问成员函数, 但容易引发空指针报错. 可以加

    if (this==NULL){
        return;
    }
    

    来解决

    4.3.4 const 修饰成员函数

    常函数:

    • 成员函数加 const 后, 称这个函数为常函数

      void fun() const{}

    • 这个const修饰的是this指针, 让指针修饰的值也不可修改

    • 常函数内不可以修改成员属性

    • 成员属性声明时加关键字 mutable 后, 在常函数中仍可以修改

    常对象:

    • 声明对象时加 const 称该函数为常对象
    • 常对象只能调用常函数

    4.4 友元

    目的: 让一个函数或者类访问另一个类中的私有成员

    三种实现:

    • 全局函数做友元

      class Building
      {
          //友元声明
          friend void fun();
          private:
          int a;
      };
      void fun(Building building)
      {
      	cout << a << endl;
      }
      
    • 类做友元

      class Building
      {
          //友元声明
          friend class C;
          private:
          int a;
      };
      
    • 成员函数做友元

      class Building
      {
          //友元声明
          friend Class::fun();
          private:
          int a;
      };
      

    4.5 运算符重载

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

    4.5.1 加号重载

    • 成员函数重载+号
    Person operator+(Person &p)
    {
        Person temp;
        temp m_A = this->m_A + p.m_A;
        temp m_B = this->m_B + p.m_B;
        return temp;
    }
    //使用
    Person p3 = p1.operator+(p2);
    //简化为
    Person p3 = p1 + p2;
    
    • 全局函数重载+号
    Person operator+(Person &p1, Person &p2)
    {
        Person temp;
        temp m_A = p1.m_A + p2.m_A;
        temp m_B = p1.m_B + p2.m_B;
        return temp;
    }
    //使用
    Person p3 = operator+(p1, p2);
    //简化为
    Person p3 = p1 + p2;
    

    在操作数类型不同时, 运算符重载也可以发生函数重载

    内置数据类型不能重载

    4.5.2 左移运算符<<重载

    通常不会用成员函数重载左移运算符, 只能用全局函数重载左移运算符

    ostream &operator<< (ostream::&cout, Person &p)
    {
        cout << "m_A = " << p.m_A << "; m_B = " << p.m_B;
        return cout;
    }
    

    若要访问私有变量就要把这个函数设为友元

    4.5.3 递增运算符重载

    类内:

    class Myinteger
    {
    public:
        int m_Num;
        MyInteger():m_Num(0){}
        
    	Myinteger& operator++()
    	{
    	    m_Num++;
    	    return this;
    	}
    };
    

    4.5.4 赋值运算符重载

    编译器自己的拷贝是浅拷贝, 在析构时可能会重复释放报错

    Person& operator=(Person &p)
    {
        //编译器提供浅拷贝
        
        //判断堆区是否有属性存在, 若存在先释放
        if(m_Age != NULL)
        {
            delete m_Age;
            m_Age = NULL;
        }
        //深拷贝
        m_Age = new int(*p.m_Age);
        //返回自身
        return *this;
    }
    

    4.5.5 关系运算符重载

    • ==
    • !=
    • <
    • >

    4.5.6 函数调用运算符重载

    • ()也可以重载
    • 重载后非常像函数调用, 称为仿函数
    • 仿函数没有固定写法, 非常灵活
    //打印输出类
    class MyPrint
    {
        public:
        //重载函数调用运算符
        void operator()(String test)
        {
            cout << test << endl;
        }
    };
    void test()
    {
        MyPrint myPrint;
        myPrint("hello world");
    }
    
    //加法类
    class MyAdd
    {
        public:
        int operator()(int num1, int num2)
        {
            return num1 + num2;	
        }
    }
    void test()
    {
        MyAdd myAdd;
        int res = myAdd(100, 100);
        cout << "res = " << res << endl;
        
        //匿名函数对象
        cout << MyAdd(100, 100) << endl;
    }
    

    21/7/3


    4.6 继承

    下级别类成员除了除了有自己的特性, 还拥有与上一级的共性. 这时我们使用继承, 减少重复代码

    继承的好处: 减少重复的代码

    语法: class 子类 : 继承方式 父类

    子类也称为 派生类, 父类也称为 基类

    class Fruit 
    {
        
    };
    //继承
    class Apple : public Fruit
    {
        
    };
    

    4.6.2 继承方式

    三种:

    class A
    {
        public:
        int a;
        protected:
        int b;
        private:
        int c;
    }
    
    • 公共继承

      class B : public A
      {
          public:
          int a;
          protected:
          int b;
          private:
      }
      
    • 保护继承

      class B : protected A
      {
          protected:
          int a;
          int b;
      }
      
    • 私有继承

      class B : private A
      {
          private:
          int a;
          int b;
      }
      

    private属性和函数三种继承都访问不到,

    4.6.3 继承中的对象模型

    private属性和函数被隐藏了, 用 sizeof 关键字可以验证, 但还是会继承下去

    vs的开发人员命令提示符 工具可以验证

    打开后输入 d: 进入d盘

    输入 cd ctrl+v 或者右键可以粘贴地址, 跳转到该文件夹下

    输入 dir 可以显示该目录下所有的文件和文件夹

    输入 cl /d1 reportSingleClassLayout类名 再输入文件名(可以输入一部分后按tab键补全)

    4.6.4 继承中的构造和析构顺序

    先构造父类 -> 后构造子类

    先析构子类 -> 后析构父类

    4.6.5 继承中同名成员的处理方式

    同名成员访问子类直接访问, 访问父类成员需要加上父类的作用域

    编译器会隐藏掉所有的同名成员函数, 必须要加作用域才能调用

    4.6.6 继承中同名的静态成员处理方式

    1. 通过对象访问: 和普通成员一样, 也是加作用域
    2. 通过类名的方式访问:
      • 父类 :: 静态成员 : 直接从父类中访问
      • 子类 :: 父类 :: 静态成员 : 从子类中访问父类

    和非静态的基本一致

    4.6.7 多继承语法

    实际开发中不建议使用多继承

    class 子类 : 继承方式 父类1, 继承方式 父类1

    4.6.8 菱形继承

    也叫钻石继承

    image-20210704175242981

    利用虚继承可以解决菱形继承的问题:

    在继承之前加上关键字 vitual 变为虚继承

    class Sheep : virtual public Animal{};

    vbptr (virtual base pointer)虚基类指针 指向 vbtable

    通过偏移量指向唯一的数据

    21.7.4


    TODO

    https://www.bilibili.com/video/BV1et411b73Z?p=135

  • 相关阅读:
    angularjs学习笔记—事件指令
    JS编写点击页面弹出被点击的标签名
    对数据进行排序
    springBoot集成seata
    maven打包时根据不同的环境生成不同的jar包名称
    单列模式-双重锁校验解析
    hashmap原理简述
    Linux搭建disconf(二)
    Linux搭建dubbo-admin 分布式服务监控中心
    Linux安装zookeeper
  • 原文地址:https://www.cnblogs.com/karlshuyuan/p/14967433.html
Copyright © 2011-2022 走看看