C++ 第 39 章: 阶段性总复习——类、对象、构造、继承、this、指针与智能指针

39.1 从变量到对象

最初:

复制代码
double angle = 30;
double speed = 10;

数据散着放。

后来:

复制代码
struct Joint
{
    double angle;
    double speed;
};

把相关数据组合。

再后来:

复制代码
class Joint
{
public:
    void setAngle(double angle)
    {
        this->angle_ = angle;
    }

private:
    double angle_ = 0.0;
};

变成:

复制代码
对象
=
数据
+
操作这些数据的函数

所以一个:

复制代码
Joint j;

对象可以粗略画成:

复制代码
j
┌────────────────────┐
│ angle_             │
│                    │
│ setAngle()         │
└────────────────────┘

39.2 class 和对象不是一回事

复制代码
class Joint
{
    ...
};

是:

类型/设计规则。

而:

复制代码
Joint j;

才是:

创建具体对象。

对应:

复制代码
class
= 图纸

object
= 按图纸造出来的东西

39.3 构造函数不是"造对象的命令"

复制代码
class Robot
{
public:
    Robot()
    {
    }
};

这里只是定义:

Robot 对象出生的时候怎么初始化。

真正:

复制代码
Robot robot;

才开始创建对象。

过程:

复制代码
Robot robot;
↓
需要创建一个Robot对象
↓
准备对象存储空间
↓
调用Robot()
↓
对象初始化
↓
robot可以使用

39.4 初始化列表

例如:

复制代码
class Robot
{
public:
    Robot(int id)
        : id_(id)
    {
    }

private:
    int id_;
};

Robot robot(3);

过程:

复制代码
参数 id = 3
↓
创建 robot
↓
直接初始化成员 id_ = 3
↓
执行 {}

所以:

复制代码
: id_(id)

不是赋值语句,而是:

初始化成员。


39.5 继承

复制代码
class Child : public Parent

表示:

Child 是 Parent 的派生类。

一个 Child 对象包含:

复制代码
Child对象
┌───────────────────┐
│ Parent父类部分    │
├───────────────────┤
│ Child自己的部分   │
└───────────────────┘

于是:

复制代码
class NumberPublisher : public rclcpp::Node

表示:

复制代码
NumberPublisher对象
┌────────────────────────┐
│ rclcpp::Node部分       │
├────────────────────────┤
│ publisher_             │
│ timer_                 │
│ count_                 │
└────────────────────────┘

39.6 父类构造

复制代码
NumberPublisher()
    : Node("number_publisher"),
      count_(0)

意味着:

复制代码
创建NumberPublisher
↓
先调用Node构造函数
Node("number_publisher")
↓
初始化count_=0
↓
执行NumberPublisher构造函数体

注意:

复制代码
Node("number_publisher")

不是另外造一个没关系的 Node。

它是在构造:

当前 NumberPublisher 对象内部的 Node 父类部分。


39.7 this 到底是什么

只要你正在:

复制代码
某个对象.成员函数()

那么这个成员函数内部就隐含一个:

复制代码
this

它指向:

当前对象。

例如:

复制代码
Joint j1;
j1.setAngle(30);

进入:

复制代码
void setAngle(double angle)

之后:

复制代码
this
 ↓
j1

于是:

复制代码
this->angle_

就是:

复制代码
j1自己的angle_

39.8 this 不是对象里的普通成员变量

不要画成:

复制代码
Joint
├── angle_
└── this   ×

更合适理解:

复制代码
调用成员函数时
C++偷偷额外告诉函数:
"当前对象地址在这里。"
        ↓
       this

这是以后理解成员函数非常关键的模型。


39.9 this 和继承

复制代码
this->create_publisher(...)

时:

复制代码
this
↓
当前NumberPublisher对象
↓
寻找create_publisher
↓
NumberPublisher自己没有
↓
父类Node有
↓
调用Node提供的函数

所以继承的功能不需要把源码重新写进子类。

子类对象能够使用继承得到的成员接口。


39.10 public / private

例如:

复制代码
class Joint
{
public:
    void setAngle(double angle)
    {
        angle_ = angle;
    }

private:
    double angle_;
};

外部:

复制代码
j.angle_ = 30;    // 不允许

但是:

复制代码
j.setAngle(30);   // 允许

然后类内部:

复制代码
angle_ = angle;

允许。

所以封装逻辑:

复制代码
外部
↓
public接口
↓
类内部逻辑
↓
private数据

39.11 对象与指针

直接对象:

复制代码
Robot robot;

访问:

复制代码
robot.run();

因为:

复制代码
robot
= 对象

指针:

复制代码
Robot* p = &robot;

此时:

复制代码
p
 ↓
robot对象

访问:

复制代码
p->run();

所以锁死:

复制代码
对象     .
指针     ->

39.12 指针最根本的作用

现阶段你只需要一句:

指针主要解决"我怎么找到另一个对象"。

它不是:

数据运输管道。

例如:

复制代码
publisher_->publish(msg);

不是:

复制代码
msg进入publisher_这个指针

而是:

复制代码
publisher_
↓
找到Publisher对象
↓
调用Publisher对象的publish()
↓
把msg作为函数参数传进去

39.13 智能指针

裸指针:

复制代码
Robot* p;

重点是:

保存地址。

智能指针:

复制代码
std::shared_ptr<Robot>

除了可以找到对象,还:

参与管理对象生命周期。

创建:

复制代码
auto p = std::make_shared<Robot>();

含义:

复制代码
创建Robot对象
+
创建shared_ptr管理这个对象
+
把智能指针返回给p

所以不是"只有指针没有对象"。

对象真实存在。


39.14 make_shared

例如:

复制代码
auto node =
    std::make_shared<NumberPublisher>();

可以拆成三个概念:

复制代码
NumberPublisher
→ 要创建的对象类型

make_shared
→ 创建对象并用shared_ptr管理

node
→ 得到的智能指针变量

关系:

复制代码
node
 │
 ▼
NumberPublisher对象

39.15 node、this、publisher_ 都是指针,但角色不同

这是目前最重要的表:

名字 指向谁 用来干什么
node NumberPublisher 对象 main() 找到并管理整个节点对象
this 当前 NumberPublisher 对象 成员函数知道"我正在操作哪个对象"
publisher_ Publisher 对象 NumberPublisher 找到自己的发布器
timer_ Timer 对象 NumberPublisher 找到自己的定时器

所以:

都是"指向对象",但它们指向的对象完全不同。


39.16 Lambda

复制代码
[this]()
{
    publishNumber();
}

分成:

复制代码
[this]

捕获列表:

让 lambda 记住当前对象的 this 指针。

复制代码
()

参数列表:

没有参数。

复制代码
{
    publishNumber();
}

函数体:

调用当前对象的成员函数。

所以 Timer 保存这个 lambda 后:

复制代码
Timer到时间
↓
执行lambda
↓
lambda拿到this
↓
找到NumberPublisher
↓
调用publishNumber()

39.17 本章最终对象图

复制代码
main()

node
 │
 │ shared_ptr
 ▼
┌───────────────────────────────┐
│ NumberPublisher对象          │
│                               │
│ Node父类部分                  │
│                               │
│ count_                        │
│                               │
│ publisher_ ──────────────┐    │
│                          │    │
│ timer_ ──────────────┐   │    │
└──────────────────────│───│────┘
                       │   │
                       ▼   ▼
                   Timer   Publisher
                   对象     对象

Timer
 │
 └─保存lambda
       │
       └─保存this
             │
             └────→ NumberPublisher对象

这一张图就是第 39 章核心。

相关推荐
wuminyu1 小时前
Virtual Thread重投递至ForkJoinPool任务队列过程解析
java·linux·c语言·jvm·c++
垆边人似月.1 小时前
华为题:污染水域
算法
All for pursuit.2 小时前
【回溯-6】79.单词搜索
数据结构·c++·算法·leetcode
掉进电商坑三年没爬出来的东叔2 小时前
青龙面板进阶:一个面板聚合多平台签到,所有积分自动领
java·开发语言
ZhangJun952 小时前
Mobike 共享单车分析项目
人工智能·python·算法·kmeans·聚类·knn
Navigator_Z2 小时前
LeetCode //C - 1281. Subtract the Product and Sum of Digits of an Integer
c语言·算法·leetcode
iCxhust2 小时前
51单片机电力载波应用开发(七)广播通讯
c语言·开发语言·单片机·嵌入式硬件·51单片机
薛定e的猫咪2 小时前
(AISTATS 2023)BaCaDI 逐章阅读
人工智能·深度学习·算法·机器学习
Benny_Tang2 小时前
AT_arc180_d [ARC180D] Division into 3 题解
数据结构·c++·算法