本文结合当前 ROS 2 项目中的 PersonNode 和 StudentNode 代码,说明 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 |
执行过程可以理解为:
- 创建
PersonNode。 - 调用
Node("person_node")。 - 初始化
name_为"蓝琪"。 - 初始化
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 |
PersonNode 的 node_name |
name_value |
PersonNode 的 name_value |
age_value |
PersonNode 的 age_value |
4. grade_(grade_value)
grade_(grade_value)
这是子类自己的成员变量初始化:
grade_:StudentNode的成员变量。grade_value:StudentNode的构造函数参数。
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)
执行顺序是:
- 先调用 PersonNode 父类构造函数
- 父类再调用 rclcpp::Node 构造函数
- 初始化父类的 name_ 和 age_
- 初始化 StudentNode 自己的 grade_
- 执行 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。
StudentNode收到name_value和age_value。- 将这两个参数传给
PersonNode。 PersonNode保存self.name和self.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 |
执行过程:
- 创建 StudentNode
- StudentNode 调用 PersonNode 的构造函数
- PersonNode 调用 ROS 2 Node 的构造函数
- 保存姓名 name
- 保存年龄 age
- 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