本文主要介绍和记录C++中静态成员变量和普通成员变量、私有成员变量和公有成员变量的区别,并给出相关示例程序,最后结合相关工程应用中编译报错给出报错原因及介绍思路
一、静态成员变量和普通成员变量
C++中,静态成员变量和普通成员变量有一些重要的区别,如下所示:
1. 存储位置:
普通成员变量: 每个类的对象都有自己的一份普通成员变量的拷贝,它们存储在对象的内存中。
静态成员变量: 所有属于同一个类的对象共享同一份静态成员变量的拷贝,它存储在类的类体外,并且不依赖于类的实例。
2. 作用域:
普通成员变量: 只有在类的实例化后,通过对象访问。
静态成员变量: 可以通过类名直接访问,不需要创建类的实例。
3. 初始化:
普通成员变量: 在每个对象被创建时进行初始化,可以在类的构造函数中进行。
静态成员变量: 需要在类体外进行初始化,通常在类的实现文件中进行。
4. 生命周期:
普通成员变量: 随着对象的创建而创建,随着对象的销毁而销毁。
静态成员变量:从程序开始时创建,直到程序结束时销毁。
5. 访问权限:
普通成员变量:可以是公有、私有或受保护的,根据访问修饰符的设置。
静态成员变量: 也可以是公有、私有或受保护的,但由于可以通过类名直接访问,一般建议将其声明为私有,并提供公有的静态成员函数用于访问或修改。
6. 使用场景:
普通成员变量: 通常用于描述对象的状态或属性。
静态成员变量:通常用于描述属于整个类而非特定对象的属性,例如计数器、共享资源等。
下面是一个简单的例子,演示了静态成员变量和普通成员变量的用法:
#include <iostream>
class MyClass {
public:
int regularVar; // 普通成员变量
static int staticVar; // 静态成员变量
// 构造函数
MyClass() {
regularVar = 0;
}
// 静态成员函数,可以访问静态成员变量和其他静态成员函数
static void staticFunction() {
staticVar++;
}
};
// 初始化静态成员变量
int MyClass::staticVar = 0;
int main() {
MyClass obj1, obj2;
obj1.regularVar = 10;
obj2.regularVar = 20;
// 访问普通成员变量,实例化后,通过对象访问
std::cout << "Regular Variable of obj1: " << obj1.regularVar << std::endl;
std::cout << "Regular Variable of obj2: " << obj2.regularVar << std::endl;
// 访问静态成员变量,不需要实例化类的对象
std::cout << "Static Variable: " << MyClass::staticVar << std::endl;
// 调用静态成员函数
MyClass::staticFunction();
std::cout << "Static Variable after calling staticFunction: " << MyClass::staticVar << std::endl;
return 0;
}
这个程序创建了一个 MyClass
类,其中包含了一个普通成员变量 regularVar
和一个静态成员变量 staticVar
。`regularVar`是普通成员变量,每个对象都有自己的副本。`staticVar` 是静态成员变量,所有对象共享同一个副本。
在 main
函数中,创建了两个对象 obj1
和 obj2
,分别设置了它们的 regularVar
值。然后,分别通过类名和对象访问了静态成员变量和普通成员变量,并调用了静态成员函数来改变staticVar的值
。
运行结果如下:
Regular Variable of obj1: 10
Regular Variable of obj2: 20
Static Variable: 0
Static Variable after calling staticFunction: 1
二、私有成员变量和公有成员变量
在C++中,私有成员变量和公有成员变量有以下主要区别:
1. 访问权限:
- 私有成员变量: 只能在类的内部访问,外部代码无法直接访问。只有类的成员函数(包括私有成员函数和公有成员函数)可以访问私有成员。
- 公有成员变量: 可以被类的外部代码直接访问,没有访问限制。
2. 封装性:
- 私有成员变量:提供了更好的封装性,隐藏了类的实现细节,外部代码无法直接修改或访问私有成员。
- 公有成员变量:破坏了封装性,外部代码可以直接修改公有成员,导致代码的不安全性和可维护性降低。
3. 安全性:
- 私有成员变量: 提高了代码的安全性,防止外部代码意外地修改类的内部状态。
- 公有成员变量:可能降低代码的安全性,因为外部代码可以直接修改类的公有成员。
4. 灵活性:
- 私有成员变量: 允许类实现者更灵活地控制类的内部状态和行为,可以在类的内部进行适当的处理。
- 公有成员变量: 提供了更直接的访问方式,但可能导致类的实现难以修改。
5. 继承和派生:
- 私有成员变量:在派生类中不可直接访问基类的私有成员。
- 公有成员变量:在派生类中可以直接访问基类的公有成员,但这同样可能导致破坏封装性。
下面是一个简单的例子,演示了私有成员变量和公有成员变量的用法:
#include <iostream>
class Example {
private:
int privateVar; // 私有成员变量
public:
int publicVar; // 公有成员变量
// 构造函数
Example() {
privateVar = 0;
publicVar = 0;
}
// 成员函数访问私有成员
void setPrivateVar(int value) {
privateVar = value;
}
int getPrivateVar() const {
return privateVar;
}
};
int main() {
Example obj;
// 外部无法直接访问私有成员
// obj.privateVar; // 编译错误
// 外部可以直接访问公有成员
obj.publicVar = 42;
// 调用成员函数访问私有成员
obj.setPrivateVar(10);
int privateValue = obj.getPrivateVar();
// 输出结果
std::cout << "Public Variable: " << obj.publicVar << std::endl;
std::cout << "Private Variable: " << privateValue << std::endl;
return 0;
}
在上述示例中,`privateVar` 是私有成员变量,只能通过成员函数进行访问,而`publicVar` 是公有成员变量,可以直接在外部访问。
运行结果如下:
Public Variable: 42
Private Variable: 10
三、相关编译报错原因及解决方法
1、编译报错内容如下:
/home/gly/catkin_ws/src/navigation-noetic-devel/rrt_star_global_planner-main/src/traj_optimizer.cpp:1066:9: error: invalid use of member ‘Trajectory_optimization::BsplineOptimizer::Opt_Storage’ in static member function
1066 | Opt_Storage.save_vector_T(opt_base_path,FN_2,time_consuming);
| ^~~~~~~~~~~
In file included from /home/gly/catkin_ws/src/navigation-noetic-devel/rrt_star_global_planner-main/src/traj_optimizer.cpp:1:
/home/gly/catkin_ws/src/navigation-noetic-devel/rrt_star_global_planner-main/include/rrt_star_global_planner/traj_optimizer.h:66:37: note: declared here
66 | Data_Storage::FileDataStorage Opt_Storage; //创建FileDataStorage类的实例,取名为Mpc_Storage
2、报错原因分析:
我们以第一个报错为例进行介绍,报错原因是在类中创建了普通成员Opt_Storage,然后尝试在类的静态成员函数 costFunctionCallback
中直接调用类的该普通成员Opt_Storage,导致报错
因为,静态函数无法直接访问非静态成员!!!
3、解决方案:
有三种解决思路,第一种是将 costFunctionCallback
设计成非静态成员函数,这样它就可以访问实例的成员变量。此时,在该函数中直接使用以下语句就不会产生报错了
Opt_Storage.save_vector_T(opt_base_path,FN_2,time_consuming);
如果我们需要保留 costFunctionCallback函数为静态成员函数,则可以采用第二种方案,使用实例化的类的对象来调用该成员,此时,上述语句需要改写成以下的形式
//由于costFunctionCallback是静态成员函数,而静态函数无法直接访问非静态成员
//需要借助类的实例来访问
Trajectory_optimization::BsplineOptimizer temporary_object;
temporary_object.Opt_Storage.save_vector_T(temporary_object.opt_base_path,temporary_object.FN_2,time_consuming);