blog icon indicating copy to clipboard operation
blog copied to clipboard

对象所有权问题

Open v4if opened this issue 7 years ago • 0 comments

先抛出一个思考的问题,对象构造的时候什么时候选择栈上对象,什么时候选择需要去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运行结果如下:

raw_pointer_test

无效的内存读取,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_ptrweak_ptr分别管理对象的所有权和检测对象的有效性,或者使用unique_ptr转移对象的所有权,必须保证在对象调用的时候,被调用对象是活着的

v4if avatar May 30 '18 06:05 v4if