本文结合当前 ROS 2 项目中的 PersonNodeStudentNode 代码,说明 Python 与 C++ 中构造函数、父类构造函数和成员变量初始化的对应关系。

先抓住三个对应关系

  • 构造函数:C++ 的 类名(...) ↔ Python 的 __init__(...)
  • 父类初始化:C++ 的 : 父类(...) ↔ Python 的 super().__init__(...)
  • 成员变量:C++ 的 成员_(参数) ↔ Python 的 self.成员 = 参数

阅读路线 先看整体对照 · 直接查速查表 · 回顾五条规则

一、先看整体对应关系

C++ 写法

类名(参数)
: 父类构造函数(参数),
  成员变量1_(参数),
  成员变量2_(参数)
{
}

例如:

PersonNode(
  const std::string & node_name,
  const std::string & name_value,
  int age_value)
: Node(node_name),
  name_(name_value),
  age_(age_value)
{
}

Python 写法

class 子类(父类):
    def __init__(self, 参数):
        super().__init__(父类需要的参数)
        self.成员变量1 = 参数
        self.成员变量2 = 参数

例如:

class PersonNode(Node):
    def __init__(self, name_value: str, age_value: int) -> None:
        super().__init__('person_node')
        self.name = name_value
        self.age = age_value

二、C++ 的 PersonNode 父类

示例文件

src/my_cpp_package/include/my_cpp_package/person_node.hpp

代码:

class PersonNode : public rclcpp::Node
{
public:
  PersonNode(
    const std::string & node_name,
    const std::string & name_value,
    int age_value)
  : Node(node_name),
    name_(name_value),
    age_(age_value)
  {
  }

protected:
  std::string name_;
  int age_;
};

1. class PersonNode : public rclcpp::Node

class PersonNode : public rclcpp::Node

含义:

  • 子类PersonNode
  • 父类rclcpp::Node

也就是说,PersonNode 继承了 ROS 2 节点的功能。

Python 对应写法:

class PersonNode(Node):

2. PersonNode(...)

PersonNode(
  const std::string & node_name,
  const std::string & name_value,
  int age_value)

这是 PersonNode 的构造函数。

参数对照:

参数 类型 含义 示例
node_name const std::string & ROS 2 节点名称 "person_node"
name_value const std::string & 人的姓名 "蓝琪"
age_value int 人的年龄 20

3. const std::string &

const std::string & node_name

拆开理解:

部分 含义
std::string C++ 字符串类型
& 引用,不复制原来的字符串
const 函数不会修改这个字符串
node_name 参数变量名

初学阶段可以先理解为:这是一个只读字符串参数。

4. : Node(node_name)

: Node(node_name)

这是 C++ 的构造函数初始化列表。

含义:调用父类 rclcpp::Node 的构造函数,并把 node_name 传给父类。

这对应 Python 中的:

super().__init__('person_node')

语法提醒:C++ 中初始化列表前面的冒号 : 不能省略。

5. name_(name_value)

name_(name_value)

含义:用参数 name_value 初始化成员变量 name_

对应关系:

  • name_:成员变量。
  • name_value:构造函数参数。

Python 对应写法:

self.name = name_value

6. age_(age_value)

age_(age_value)

含义:用参数 age_value 初始化成员变量 age_

Python 对应写法:

self.age = age_value

7. C++ 成员变量末尾的下划线

std::string name_;
int age_;

这里的下划线不是语法要求,而是一种常见命名习惯:

  • name_value:普通参数。
  • name_:类的成员变量。

这样可以清楚区分参数和成员变量。

三、C++ 中创建 PersonNode 对象

auto person = std::make_shared<PersonNode>(
  "person_node",
  "蓝琪",
  20);

参数对应:

传入位置 实际值 对应参数
第 1 个参数 "person_node" node_name
第 2 个参数 "蓝琪" name_value
第 3 个参数 20 age_value

执行过程可以理解为:

  1. 创建 PersonNode
  2. 调用 Node("person_node")
  3. 初始化 name_"蓝琪"
  4. 初始化 age_20

四、C++ 的 StudentNode 子类

示例文件

src/my_cpp_package/src/student_node.cpp

核心代码:

class StudentNode : public PersonNode
{
private:
  int grade_;

public:
  StudentNode(
    const std::string & node_name,
    const std::string & name_value,
    int age_value,
    int grade_value)
  : PersonNode(node_name, name_value, age_value),
    grade_(grade_value)
  {
  }
};

1. class StudentNode : public PersonNode

class StudentNode : public PersonNode

含义:

  • 子类StudentNode
  • 父类PersonNode

因此 StudentNode 可以使用父类中的:

eat()
sleep()
name_
age_

2. StudentNode 的参数

StudentNode(
  const std::string & node_name,
  const std::string & name_value,
  int age_value,
  int grade_value)

参数对照:

参数 类型 含义 示例
node_name const std::string & 学生节点名称 "student_node"
name_value const std::string & 学生姓名 "蓝琪"
age_value int 学生年龄 20
grade_value int 学生年级 3

3. PersonNode(node_name, name_value, age_value)

: PersonNode(node_name, name_value, age_value)

含义:调用父类 PersonNode 的构造函数。

父类需要三个参数:

PersonNode(
  node_name,
  name_value,
  age_value)

所以子类需要把对应参数传给父类。

对应关系:

StudentNode 的参数 传给 PersonNode 后对应的参数
node_name PersonNodenode_name
name_value PersonNodename_value
age_value PersonNodeage_value

4. grade_(grade_value)

grade_(grade_value)

这是子类自己的成员变量初始化:

  • grade_StudentNode 的成员变量。
  • grade_valueStudentNode 的构造函数参数。

Python 对应写法:

self.grade = grade_value

5. 子类完整初始化顺序

StudentNode(
  node_name,
  name_value,
  age_value,
  grade_value)
: PersonNode(node_name, name_value, age_value),
  grade_(grade_value)

执行顺序是:

  1. 先调用 PersonNode 父类构造函数
  2. 父类再调用 rclcpp::Node 构造函数
  3. 初始化父类的 name_ 和 age_
  4. 初始化 StudentNode 自己的 grade_
  5. 执行 StudentNode 构造函数体

五、C++ 创建 StudentNode 对象

auto student = std::make_shared<StudentNode>(
  "student_node",
  "蓝琪",
  20,
  3);

参数对应:

传入位置 实际值 对应参数
第 1 个参数 "student_node" node_name
第 2 个参数 "蓝琪" name_value
第 3 个参数 20 age_value
第 4 个参数 3 grade_value

六、Python 的 PersonNode 父类

示例文件

src/my_package/my_package/my_python_node.py

class PersonNode(Node):
    def __init__(self, name_value: str, age_value: int) -> None:
        super().__init__('person_node')
        self.name = name_value
        self.age = age_value

Python 参数对照

参数 类型标注 含义 示例
name_value str 人的姓名 '蓝琪'
age_value int 人的年龄 20

Python super().__init__()

super().__init__('person_node')

含义:调用父类 Node 的构造函数。

注意'person_node' 是 ROS 2 节点名称。

它不是人的姓名。

Python 成员变量

self.name = name_value
self.age = age_value

对应 C++:

name_(name_value),
age_(age_value)

七、Python 的 StudentNode 子类

示例文件

src/my_package/my_package/student_node.py

class StudentNode(PersonNode):
    def __init__(
        self,
        name_value: str,
        age_value: int,
        grade_value: int
    ) -> None:
        super().__init__(name_value, age_value)
        self.grade = grade_value

Python 子类参数对照

参数 类型 含义 示例
name_value str 学生姓名 '蓝琪'
age_value int 学生年龄 20
grade_value int 学生年级 1

Python 调用父类构造函数

super().__init__(name_value, age_value)

含义:把子类收到的姓名和年龄传给父类 PersonNode

  1. StudentNode 收到 name_valueage_value
  2. 将这两个参数传给 PersonNode
  3. PersonNode 保存 self.nameself.age

Python 初始化自己的成员变量

self.grade = grade_value

grade 是学生独有的属性,父类 PersonNode 不需要知道学生年级。

八、Python 创建 StudentNode 对象

node = StudentNode('蓝琪', 20, 1)

参数对应:

传入位置 实际值 对应参数
第 1 个参数 '蓝琪' name_value
第 2 个参数 20 age_value
第 3 个参数 1 grade_value

执行过程:

  1. 创建 StudentNode
  2. StudentNode 调用 PersonNode 的构造函数
  3. PersonNode 调用 ROS 2 Node 的构造函数
  4. 保存姓名 name
  5. 保存年龄 age
  6. StudentNode 保存年级 grade

九、C++ 与 Python 写法对照表

功能 C++ Python
定义子类 class StudentNode : public PersonNode class StudentNode(PersonNode):
构造函数 StudentNode(...) def __init__(self, ...):
调用父类构造函数 PersonNode(...) super().__init__(...)
父类初始化列表 : PersonNode(...) super().__init__(...)
初始化成员变量 grade_(grade_value) self.grade = grade_value
成员变量 int grade_; self.grade
调用成员函数 student->study() node.study()
字符串类型 std::string str
整数类型 int int
无返回值 void None
继承父类成员 name_ self.name

十、最重要的规则

规则 1:父类必须先构造

C++:

: PersonNode(node_name, name_value, age_value)

Python:

super().__init__(name_value, age_value)

规则 2:参数数量必须匹配

父类定义:

def __init__(self, name_value, age_value):

正确写法

super().__init__(name_value, age_value)

错误写法 · 用于对照

super().__init__('student_node')

因为少传了 age_value

规则 3:C++ 初始化列表前必须有冒号

正确写法

PersonNode(...)
: Node(node_name),
  name_(name_value),
  age_(age_value)
{
}

错误写法 · 用于对照

PersonNode(...)
Node(node_name)

规则 4:C++ 成员变量要用初始化列表初始化

推荐写法

: name_(name_value),
  age_(age_value)

Python 对应:

self.name = name_value
self.age = age_value

规则 5:ROS 2 节点名称和对象属性不是一回事

  • 节点名 node_name:ROS 2 节点名称。
  • 姓名 name_value:人的姓名。
  • 年龄 age_value:人的年龄。

例如:

PersonNode("person_node", "蓝琪", 20)

含义是:

  • ROS 2 节点名person_node
  • 人的姓名:蓝琪
  • 人的年龄20