C++ 突袭

参考视频1:https://www.bilibili.com/video/BV1ZT4y1C7WR/?p=24&spm_id_from=333.788.top_right_bar_window_history.content.click
参考视频2:https://www.bilibili.com/video/BV15e411V7W9?p=30&vd_source=ecc5d7a46ba5b1bb791ce5ea5a39fe39

基本知识

image

image

image

image

image

  • 必须在函数声明中声明默认参数!
  • 默认参数必须在形参列表的结尾!避免歧义
  • 函数声明就是函数的身份证,外部调用方不看定义只看声明

image

image

  • 和 取地址 类似

image
image

结构体

  • 第一种是最基本的结构体定义,其定义了一个结构体A。
  • 第二种则是在定义了一个结构体B的同时定义了一个结构体B的变量m。
  • 第三种结构体定义没有给出该结构体的名称,但是定义了一个该结构体的变量n。
    也就是说,若是想要在别处定义该结构体的变量是不行的,只有变量n这种在定义结构体的同时定义变量才行。
//第一种
struct A {
    int a;
};
//第二种
struct B{
    int b;
} m;
//第三种
struct{
    int c;
} n;
  • 第四种结构体定义在第一种结构定义的基础上加了关键字typedef,此时我们将struct D{int d}看成是一个数据类型,但是因为并没有给出别名,直接用D定义变量是不行的。如D test;,不能直接这样定义变量test。但struct D test;可行。

  • 第五种结构体定义在第四种结构体定义的基础上加上了别名x,此时像在第四种结构体定义中说得那样,此时的结构体E有别名x,故可以用x定义E的结构体变量。用E不能直接定义,需要在前面加struct,如struct E test;。

  • 第六种结构体定义在第五种的基础上减去了结构体名,但是若是直接使用y来定义该结构体类型的变量也是可以的。如y test;。(常用)

//第四种  struct = D{ int d; }
typedef struct D{
    int d;
};
//第五种 struct E{ int e; } = x
typedef struct E{
    int e;
} x;
//第六种 struct { int f; } = x
typedef struct {
    int f;
} y;

image

B选项应该换行 1) #define AA struct aa 2) AA { int n; float m; }td1;

函数

overload 函数重载

image

要求:

  1. 函数的名字相同
  2. 参数列表(数量,类型)不同

注意:与返回值无关

image

问题 --避免overload歧义
image
image

image

inline 内联函数

image

内联函数:指建议编译器编译时将某个函数在调用处直接展开,避免运行时调用开销。

内联函数的要求:若一函数功能简单,则函数调用的额外开销占比较高。

image

注意:inline只是建议

  • 并不是写了 inline 关键字就一定会被内联,只是提出建议,由编译器决定是否采纳
  • 内联这个动作发生在编译时,提升运行时的效率
    image

面向对象

面向对象:既然随着系统参与实体的增多,过程变得复杂,那就不费力描述每一个可能的过程了,转而描述每一个实体
实体=属性+行为

从面向过程到面向对象 对于问题:求解不同图形的周长和面积
image

image

抽象:class 类和对象

面向对象的四个特征之1:抽象=分析问题,识别出各个实体及其属性和行为
每个实体 = 一个类class = 定义它的属性(成员变量) + 行为(成员函数)
image
例如:

class Circle {
private:
    double radius;
public:
    Circle(double radius) {
        this->radius = radius;
    }
    double getRadius() {
        return radius;
    }
};

简单理解:结构体 + 行为(成员函数) = 类

  • 事实上C++中也支持结构体定义成员方法,两者并无本质区别
  • 根据使用场景选择结构体或类:
    •结构体:主要记录数据,极少行为(如资源配置信息、网络连接信息等)
    •类:既有属性也有行为(如学生类、用户类、玩家类等)

特殊的成员函数:构造函数与析构函数

image
constructor 构造函数:对象实例化时,分配空间后,完成对象的构造工作(如初始化成员变量、分配资源等)
destructor 析构函数:对象生命周期结束时,回收空间前,完成对象的清理工作(如释放资源等
image

this 指针

• this的中文含义:这、这个、当前这个
• this指针在类定义内部使用,指向当前对象

  • 只有非静态类成员才有this指针。

this指针保证每个对象拥有自己的数据成员,但共享处理这些数据成员的代码。(√)

image

封装 Encapsulate

封装:将类的一些成员变量或方法藏起来,不允许外界直接操作

  • 不允许直接操作 ≠ 不允许操作,而是通过自定义的特定方法操作

image

getter / setter 方法
为某些私有成员变量提供外部读写方法:get_xxx(读) / set_xxx(写)

  • getter 和 setter 一般是 public 的,不然没意义
  • getter和setter函数的名字没有要求,只是一般用get_xxx,不是特殊函数。

getter函数通常会被设置为const函数,setter函数则通常接收const参数

补 const常成员函数:不能修改类成员变量,如果修改了,回过不了编译

//getter函数的通常格式(设xxx的类型为T)
T get_xxx() const {
   return xxx;
}
//setter函数的通常格式(设xxx的类型为T)
void set_xxx(const T& xxx) {
    this->xxx = xxx;
}

继承和多态

继承

人和学生之间是层次递进关系,而非并列关系:若一个实体是学生,则一定是人
• 那么在定义学生类时,无需把属于人的那一部分属性和方法再定义一遍
• 直接让学生继承自人:获得人的属性和方法
• 目的?代码复用

父类和子类:不同身份之间的层次递进关系

  • 张华首先是一个人,具体一点是一个学生,再具体一点是大学生...
  • 人 ← 学生 ← 大学生 ← ...

类的派生与继承 Inherit

image

继承方式:决定父类成员在子类中的访问控制属性

  • 派生类继承了基类的private成员,但是不能直接访问,只能通过派生类的友元函数访问
  • 公有继承不改变控制属性,保护继承和私有继承指示父类成员在子类中的相应控制属性

image

image

父子同名成员并存

  • 子类中同时有两个n和两个func()
  • 直接使用默认指子类成员
  • 如果需要使用父类的成员,需要使用父类名字空间显式指明
    image
class Father {
public:
    int n = 1;
    void func() {
        cout << "This is Father";
    }
};

class Son : public Father {
public:
    int n = 2;
    void func() {
        cout << "This is Son";
    }
    void set() {
        Father::n = -1;
        n = -2;
    }
};
int main(){
  Son son;
  son.func();
  son.Father::func();
  son.set();
  cout << son.Father::n;
  cout << son.n;
}

image
分析:

  • 此处问的是“通过派生类对象访问”,意即在外部以a.xxx的方式访问。
  • 如果题目问“派生类能访问的”,则意指在类定义内部访问,则除了基类私有成员以外的都可访问
    image

virtual 虚函数 和虚类

回顾:继承来源于同一对象可能有多重身份,且这些身份有层级递进关系
• 实践中会有这类情况:
• 父类中的某些行为需要在子类中被更加具体地细化
• 父类中的某些行为不可确定,必须在子类中实现

于是虚函数的概念产生:父类的虚函数可以在子类中被重写(override),即重新实现,但参数和返回值必须保持一致!

此外含有虚函数的类叫做虚类
image

image
分析:

  • 子类重写基类的虚函数时,可以继续通过virtual关键字申明为虚函数,表明允许被它的子类(孙子)继续重写,
  • 如果没有这个需要或者不允许继续重写则不加

纯虚函数、抽象类和接口

纯虚函数:不实现,仅声明为纯虚函数,留待子类里重写定义

因为 某些类是抽象的,不是具体的,不可独立存在:
•我可以是男人或女人,但不可能仅是“人类对象”
•可以有矩形对象、三角形对象... 但不能有“图形对象”

含有纯虚函数的类叫抽象类仅有纯虚函数的类叫接口

注意: 抽象类和接口不可实例化
image

image
分析:
“建立对象”指实例化,因为抽象类中的纯虚函数没有定义(留给子类来实现),所以抽象类不能实例化

虚继承

https://blog.csdn.net/crystal_avast/article/details/7678704

友元函数

  1. 友元只是破坏了类的隐藏性和封装性,不能被继承没有this指针没有破环继承性机制
  2. 可以直接调用,不需要通过对象或者指针。

image

多态 Polymorphism

面向对象的四个特征之4:多态

• 已多次强调:同一对象可以有多重层级递进身份
• 有这种情况:同一对象在不同的场合中,被外界所关注的是不同的身份
• 但他的本质和应有的行为并不会因外界眼光而改变

理解多态:

  • 一个对象就是内存中的一个实体,它只能属于一个确定的类:最精确的子类
  • 它可能在不同处被视为不同身份,但它本质行为方式应与外界如何看待它无关!

问题:如何保证一个对象执行其最本质身份的行为?

  • 利用虚函数重写 + 指针!!
  • 指向子类对象的父类指针!!!

image

image

多态的意义:代码复用

  • 通过“虚函数 + 指向子类对象的父类指针”,可以把不同的子类统一视为其共同父类
  • 于是无需针对不同的子类写相同逻辑,统一视作其共同父类,利用指针操作即可
  • 本质是虚函数将能做什么和怎么做分离,父类指定要做什么,子类来实现具体做法

问题:写一个函数,求一个图形的面积和周长之比
• 没有多态:需要为每种图形都实现一个函数
• 有多态:只需实现一个函数
• 它不关心这个图形具体是什么,反正能求面积和周长即可

image

静态联编与动态联编

• 上述利用虚函数重写+指针实现的多态特指运行时多态,与之相对的是编译时多态

联编(bind):确定具体要调用多个同名函数中的哪一个

  • 静态联编:在编译时就确定了要调用的是哪个函数(根据多个重载函数的参数列表确定)
  • 动态联编:直到运行时才知道实际调用的是哪个函数(根据指针指向对象的实际身份)

也就说:

  • 静态联编 = 编译时多态 = 函数重载 = overload
  • 动态联编 = 运行时多态 = 虚函数重写 = override

运算符重载

image

https://www.cnblogs.com/kingwz/p/16554387.html

struct node {
    int aa;
    int bb;
    int cc;

    // bool operator<(const node &x) const {
    //     return true;
    // }
    bool operator<(const node& x) const {
        return true;
    }
};
posted @ 2023-08-19 18:01  kingwzun  阅读(39)  评论(0编辑  收藏  举报