场景
- 在
Rust
里有不可变变量,不可变变量可以保证编译器内存安全,禁止数据竞争;并且不可变可以安全的跨线程共享,无需锁。那么C/C++
对象有这种不可变变量吗?
说明
-
首先说下简单类型是可以通过
const
来修饰不可变特性的。 -
对象类型结构的不可变特性。先说
C
肯定是没有的,C
的结构体都是public
结构,想要让成员不可变,只能通过const
来修饰成员变量,但是如果修饰了,也不能改了,虽然可以通过const_cast<int*>
运算符来移除const
,但是得到的变量如果想修改,结果是未定义的。这个运算符是为了兼容旧代码用的,现代编程这个运算符没用。 -
C++
对象在严格的声明成员变量结构时是可以做到的rust
不可变变量的效果。 它的成员变量需要声明在private
里,之后声明public
的get
,set
方法。注意get
方法必须声明const
,这样可以让const
对象和非const
对象调用。而不要声明const
的set
方法,在const
调用时会编译报错。如:编译错误,setValue不支持const Base类型
。这样声明的方式就是防止成员变量在const
对象时被直接修改,因此在有const
对象时,只能调用get
方法,不然会编译报错。
class Base
{
public:
Base() {
}
Base(const Base& base) {
value_ = base.value_;
str_ = base.str_;
}
Base(Base&& base) {
value_ = base.value_;
str_ = move(base.str_);
base.value_ = 0;
}
void setValue(int value) {
value_ = value;
}
int getValue() const {
return value_;
}
void setStr(const string& str) {
str_ = str;
}
const string& getStr() const{
return str_;
}
private:
string str_;
int value_ = 100;
};
- 如果 const 变量指向可变数据(如 const T* 指向非 const 数据),可能引发竞态条件。 所以
const
不要指向非const
对象。如果一个非const
想转换为const
,那么可以用std::move
直接把所有权移动给const
对象,原本的对象数据也就失效了。
Base base3;
base3.setValue(101);
base3.setStr("hello");
const Base base5 = move(base3);
- 除了以上说的严谨声明类结构,
C++
还有其他常量限制的特性。如常量指针,指针常量和constexpr
。
- 常量指针,初始化后不可修改指针指向的值;指针地址可变;
- 指针常量, 指针地址不可变;指针指向的值可改;
- 编译器常量,可以计算出简单语句
例子
#include <iostream>
#include <string>
#include <sstream>
#include <thread>
#include <vector>
#include <functional>
#include <type_traits>
using namespace std;
//template<class T>
//static bool IsConst(T t) {
// return is_const<decltype(t)>();
//}
template<class T>
static bool IsConstV(T& t) {
return is_const<std::remove_reference_t<decltype(t)>>();
}
template<class T>
static bool IsConstV(T* t) {
return is_const<std::remove_pointer_t<decltype(t)>>();
}
class Base
{
public:
Base() {
}
Base(const Base& base) {
value_ = base.value_;
str_ = base.str_;
}
Base(Base&& base) {
value_ = base.value_;
str_ = move(base.str_);
base.value_ = 0;
}
void setValue(int value) {
value_ = value;
}
int getValue() const {
return value_;
}
void setStr(const string& str) {
str_ = str;
}
const string& getStr() const{
return str_;
}
private:
string str_;
int value_ = 100;
};
void TestConstPointerConst()
{
// 常量指针,初始化后不可修改指针指向的值;指针地址可变;
int number = 100;
const int *pNumber = &number;
//*pNumber = 101; // 编译错误,表达式必须是可修改的左值
cout << "pNumber: " << *pNumber << endl;
// 指针常量, 指针地址不可变;指针指向的值可改
int *const pNumber2 = &number;
*pNumber2 = 101;
cout << "number: " << number << endl;
//pNumber2 = NULL; // 表达式必须是可修改的左值
// 编译器常量,可以计算出简单语句
// 256
constexpr int64_t offset = 1 << 8;
cout << "offset: " << offset << endl;
}
void Add(const string& str)
{
const int i = 9;
using myType = decltype(str);
using myType2 = decltype(i);
const string* p = new string("hello");
cout << IsConstV(i) << endl;
cout << IsConstV(str) << endl;
cout << IsConstV(p) << endl;
string str2;
string& str3 = str2;
string* p2 = &str2;
cout << "===================" << endl;
cout << IsConstV(str2) << endl;
cout << IsConstV(str3) << endl;
cout << IsConstV(p2) << endl;
cout << "===================" << endl;
const Base* base = new Base();
Base* base2 = new Base();
cout << base->getValue() << endl;
cout << base2->getValue() << endl;
delete base;
delete base2;
////base->setValue(100); // 编译错误,setValue不支持const Base类型
Base base3;
base3.setValue(101);
const Base base4;
//base4.setValue(102); // 编译错误,setValue不支持const Base类型
const string str23;
//str23.append("hello"); // 编译错误,
cout << str23 << endl;
// 1. 如果要多线程共享读取,那么把变量移动给新的不可变量。
// 如果没有重载Base(Base&& base)构造函数,那么只会是复制数据。
base3.setStr("hello");
const Base base5 = move(base3);
cout << "base5 address: " << (int)&base5 << endl;
// 不可变变量,可以多线程访问。
vector<thread> ts;
for (int i = 0; i < 3; ++i) {
ts.emplace_back(bind([](const Base& base) {
stringstream ss;
ss << "base address: " << (int)&base << "\n";
ss << "Thread: ->" << base.getValue() << "\n";
ss << "Thread: ->" << base.getStr() << "\n";
//base.setValue(101); // 编译错误,setValue不支持const Base类型
cout << ss.str() << endl;
},std::ref(base5)));
}
for (int i = 0; i < ts.size(); ++i)
ts.at(i).join();
}
int main()
{
std::cout << "Hello World!\n";
std::string str("Peter");
Add(str);
TestConstPointerConst();
}
输出
Hello World!
1
1
1
===================
0
0
0
===================
100
100
base5 address: 10418828
base address: 10418828
Thread: ->101
Thread: ->hello
base address: 10418828
Thread: ->101
Thread: ->hello
base address: 10418828
Thread: ->101
Thread: ->hello
pNumber: 100
number: 101
offset: 256