对象所有权问题
先抛出一个思考的问题,对象构造的时候什么时候选择栈上对象,什么时候选择需要去new一个对象,对象在作为参数传递的时候,谁来保证对象的有效性,所有权如何传递?
对象的构造分为栈上对象和堆上对象。
栈对象
在对栈上的对象也即局部变量对象进行函数参数传递的时候,无非两种选择:值传递或者引用传递。值传递的情况比较简单,会增加数据拷贝的开销,对象越大,付出的代价就会越大,但是不会涉及所有权的问题,在调用者和被调用函数内部对象永远都是有效的,相当于建立了一个对象的副本,同样也不会对原有对象进行更改。因此值传递会带来两个问题:拷贝带来的性能代价和对形参的操作并不会对实参有所改变。
引用传递不会拷贝对象,好像可以避免值传递所带来的问题,但是在某些情况下却会有对象有效性的问题,比如异步回调中局部变量对象的引用
void timer_async_wait(boost::asio::io_service& ios) {
int a = 1;
boost::asio::steady_timer timer(ios);
timer.expires_from_now(std::chrono::seconds(2));
timer.async_wait([&a](const boost::system::error_code& error){
std::cout << a << std::endl;
});
}
void local_ownship_test() {
boost::asio::io_service ios;
timer_async_wait(ios);
ios.run();
}
timer.async_wait的回调闭包中绑定了局部变量a的引用,但是当回调lambda真正执行的时候,栈上的对象a已经被析构了,再对a进行操作属于UB行为。是否需要对引用传递的对象进行有效性判断,个人认为是有必要的,但是怎么判断好像是个死区。因此才有了new一个对象,然后通过智能指针进行管理,如果对象需要共享,则通过shared_ptr<T>和weak_ptr<T>去判断被管理对象的有效性
堆对象
堆对象就是通过new产生的对象,堆对象管理方式可以通过原生指针和智能指针,很明显在对堆对象进行函数参数传递的时候,一般都是对管理对象进行传递,也即传递原生指针或智能指针。
堆对象需要共享的话,有效性检查是一个必须要做的工作!如果使用原生指针进行传递好像又是一个无解的行为,比如下面的代码:
void raw_pointer_out(int* a) {
std::cout << *a << std::endl;
}
void raw_pointer_test() {
int* a = new int(1);
delete a;
raw_pointer_out(a);
}
用valgrind运行结果如下:

无效的内存读取,UB行为。那么使用原生指针,在delete指针的时候对指针进行赋值nullptr+判断是否为nullptr来确定对象的有效性可行吗?
看似可行,但是没有考虑下面这种情况:
void raw_pointer_out(int* a) {
if (a != nullptr) {
std::cout << *a << std::endl;
} else {
std::cout << "nullptr" << std::endl;
}
}
int* delete_and_set_nullptr(int* a) {
delete a;
return nullptr;
}
void raw_pointer_test() {
int* a = new int(1);
int* fuck = a;
fuck = delete_and_set_nullptr(fuck);
raw_pointer_out(a);
}
a不为nullptr但是指向的对象已经被删除了
传递对象的所有权, 开销比复制来得小, 如果可以复制的话. 传递所有权也比”借用”指针或引用来得简单, 毕竟它大大省去了两个用户一起协调对象生命周期的工作.
因此得出的结论是:
1、除了非共享的变量一律创建堆上的对象
2、对象共享的时候使用shared_ptr和weak_ptr分别管理对象的所有权和检测对象的有效性,或者使用unique_ptr转移对象的所有权,必须保证在对象调用的时候,被调用对象是活着的