c++ - 参考 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,B是App
类,A 是EventHandler
在主函数中实例化的类。
正如@πάντα-ῥεῖ 在我的特殊情况下注意到的那样,唯一合理的方法是使用智能指针,因为我正在尝试使用 std::thread 和 std::atomic,但在最一般的情况下,最好的方法是替换旧的 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;
)
推荐阅读
- css - 光滑的滑块在其他元素下移动
- python - 为什么我在 python 列表中出现索引错误?
- c - 任何可用于 C 中无符号字符指针行为的文档?
- react-native - TypeError:未定义不是对象 - AsyncStorage
- c++ - QColor 到人类可读的字符串
- python - 根据星期几查找每小时平均数据,以模拟缺失日期的数据
- pandas - Pandas Dataframe 使用分类数据创建 Seaborn 水平条形图
- angular - 将新项目推送到数组时,离子虚拟滚动未更新
- css - 如何在 Bootstrap 4 中使右列高度独立?
- r - 如何添加自定义 R 闪亮输入按钮?(最小的例子)