首页 > 解决方案 > 参考 vs smart_ptr 用于依赖倒置

问题描述

我有两种数据类型,A 和 B,我必须将 A 实例存储在 B 中,以便在 B 方法中引用 A 实例。两者都在主函数中实例化,因此只要程序运行,这两个变量就会存在。

我不想复制,我想使用类似老式指针的东西,但我也想遵循(并学习)现代 C++ 最佳实践。那么将 A 实例存储在 B 中的最合适的类型是什么?(为什么?)

我认为如果我想使用指针,现代 C++ 的最佳方法是使用智能指针,但引用似乎更容易和更轻松,那么引用在范围内创建的变量的最佳做法是什么(例如在main 函数)并在另一个作用域中使用(例如,具有指向该变量的指针的类的方法),知道只要该类存在,创建该变量的作用域就会存在(当堆栈被释放时,它将释放变量和具有变量引用的对象)?

struct A {};

struct B {
 A & a;
 B(A & a) : a(a) {}
};

int main() {
  A a{};
  B b{a};
}

或者

#include<memory>

struct A {};

struct B {
 std::shared_ptr<A> a;
 B(auto a) : a(a) {}
};

int main() {
  B b{std::make_shared<A>()};
}

我正在制作的程序本质上是一堆学习 SDL2 的测试,我在这里发布了 repo https://github.com/antcolag/prova-sdl,BApp类,A 是EventHandler在主函数中实例化的类。

正如@πάντα-ῥεῖ 在我的特殊情况下注意到的那样,唯一合理的方法是使用智能指针,因为我正在尝试使用 std::thread 和 std::atomic,但在最一般的情况下,最好的方法是替换旧的 C 风格指针,采用更现代的方法,当变量在堆栈中分配,由其他对象使用,然后与对象一起从堆栈中释放?

标签: c++

解决方案


如果我理解这个问题,你想将实例移动到不同的“所有者”,它是可能的,但如果实例的范围被删除,A a;它需要一个。memcpy()最简单的解决方案是将其包含在共享范围中,这很糟糕,因为它可以是全局范围,下一个最好的方法是将引用传递给所有者(包含数据的结构)。最后,如果它们被反复应用,这是便宜的解决方案,现代 C++ 有很多用于内存控制/流的工具;其中大多数是基于指针的,因为数据指针复制很简单,请注意,只有与 std::atomic 或类似的库结合使用才适用于多线程。

这个例子展示了如何在没有任何花哨的 c++ 的情况下移动和使用数据指针,关于指针思想的一个小说明,在这个例子中,指针地址不会改变,只要它没有被删除,任何引用都会保持即使 ref_objs顺序改变了,数据是“on the wild”,指针是一个数字。

#include <iostream>

struct Object {
    int num = 69;
};

struct Container {

    // Better to use std::vector but
    // this shows better what it does
    // Olso can be replaced with 
    // Object * ref_objs [n] if n is fixt
    Object ** ref_objs;
    
    uint32_t n_obj;
    uint32_t n_obj_max;

    void provision_for(uint32_t res_len){
        // To initialize data is better to use
        // use a method insted of the constructor;
        // This alocates n spaces of obj pointers
        ref_objs = new Object * [res_len];
        n_obj_max = res_len;
        n_obj = 0;
    }

    void clear_all(){
        uint32_t i;
        for (i=0; i < n_obj; i++){
            delete ref_objs[i];
        }
        delete [] ref_objs;
        n_obj = 0;
    }

    Object * add_obj(){
        Object * ret = nullptr;
        if (n_obj < n_obj_max){
            ref_objs[n_obj] = new Object;
            ret = ref_objs[n_obj];
            n_obj++;
        }
        return ret;
    }

    void del_obj(uint32_t n){
        if (n < n_obj - 1){
            // keeps them alighned
            ref_objs[n] = ref_objs[n_obj];
        }
        delete ref_objs[n_obj];
        n_obj--;
    }
    int recive_obj(Object * ref){
        int res = 1;
        if (n_obj < n_obj_max){
            ref_objs[n_obj] = ref;
            n_obj++;
            res = 0;
        }
        return res;
    }
    int transfer_to(Container * to, uint32_t item){
        int res = 1;
        if (to->recive_obj(ref_objs[item]) == 0){
            if (item < n_obj - 1){
                ref_objs[item] = ref_objs[n_obj - 1];
            } else {
                ref_objs[item] = nullptr;
            }
            n_obj --;
            res = 0;
        }
        return res;
    }

    Object * at (uint32_t at){
        return ref_objs[at];
    }
    Object & operator [](uint32_t at){
        // [0] is added to asure the compiler that it
        // is a instance and not an array
        return ref_objs[at][0];
    }

};


int main(void){

    Container container_a;
    Container container_b;

    container_a.provision_for(10);
    container_b.provision_for(15);

    Object * x = container_a.add_obj();
    Object * y = container_a.add_obj();
    Object * z = container_b.add_obj();
    std::cout << "container_a len -> " << container_a.n_obj << std::endl;
    std::cout << "container_b len -> " << container_b.n_obj << std::endl;

    y->num = 200;
    container_a.transfer_to(&container_b, 0);
    container_b[1].num = 400;
    std::cout << "container_a obj[0].num -> " << container_a[0].num << std::endl;
    std::cout << "container_b obj[0].num -> " << container_b[0].num << std::endl;
    std::cout << "container_b obj[1].num -> " << container_b.ref_objs[1]->num << std::endl;

    container_a.del_obj(0);
    container_a.clear_all();
    container_b.clear_all();

    return 0;
}

(此示例适用于模板,只需将所有类型更改Object为 typename即可Container<Object> container_a;


推荐阅读