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

posted @ 2021-07-03 20:13  卡尔书院  阅读(405)  评论(0编辑  收藏  举报