c++ - 使用引用计数智能指针的前向声明
问题描述
我已经实现了一个类,该类reference<T>
跟踪对T
从reference_countable
.
我有一个关于转发声明的T
问题reference<T>
,类似于std::unique_ptr<T>
. std::unique_ptr<T>
问题来自于不知道的析构函数,因此您只需将类的析构函数放入 cpp 文件中,如下所示:
标题:
class MyClass;
class A
{
std::unique_ptr<MyClass> my_class;
}
执行:
A:~A() = default;
但是,在我的版本中, wherestd::unique_ptr<MyClass>
替换为reference<MyClass>
,reference_countable
也必须递减和递增。这要求我在仅前向声明时也将复制分配和复制构造函数A
放入 cpp 文件中。MyClass
有没有办法避免将这三个函数的实现放在所有具有reference<T>
成员的类的 cpp 文件中?
我试图尽可能简单地描述它,但要了解更多细节,这里是问题的简单版本。具体来说,需要A
在a.cpp中定义copy-constructor。
my_class.h
#pragma once
#include "minimal_ref_counter.h"
class MyClass : public minimal_reference_countable
{
};
啊
#pragma once
#include "minimal_ref_counter.h"
class MyClass;
class A
{
public:
A();
~A();
A(const A&);
minimal_reference_counter<MyClass> my_class;
};
a.cpp
#include "test.h"
#include "myclass.h"
A::A()
: my_class(new MyClass())
{}
A::~A() = default;
A::A(const A&) = default;
some_other_code.cpp
#include "a.h"
void some_function()
{
A a1;
A a2 = a1; // this code does not compile without the copy assignment operator beeing implemented externally.
}
minimum_ref_counter.h
#pragma once
#include <atomic>
class minimal_reference_countable
{
template<typename T>
friend class minimal_reference_counter;
std::atomic_int m_references = 0;
auto reference_count() const { return m_references.load(); }
void decrement() { --m_references; }
void increment() { ++m_references; }
};
template<typename T>
class minimal_reference_counter
{
public:
minimal_reference_counter(T* t = nullptr)
{
assign(t);
}
~minimal_reference_counter()
{
reset();
}
minimal_reference_counter(const minimal_reference_counter& r)
{
*this = r;
}
minimal_reference_counter(minimal_reference_counter&& r)
{
*this = std::move(r);
}
minimal_reference_counter& operator=(const minimal_reference_counter& r)
{
assign(r.m_ptr);
return *this;
}
minimal_reference_counter& operator=(minimal_reference_counter&& r)
{
assign(r.m_ptr);
r.reset();
return *this;
}
void reset()
{
if (!m_ptr) return;
m_ptr->decrement();
if (m_ptr->reference_count() == 0)
{
delete m_ptr;
}
m_ptr = nullptr;
}
private:
void assign(T* ptr)
{
reset();
m_ptr = ptr;
if (m_ptr) m_ptr->increment();
}
T* m_ptr = nullptr;
};
解决方案
C++ 模板是惰性的。实例化被推迟到必要时。当你有一个不完整类型的成员,稍后完成时,将析构函数放入 cpp 文件的原因是std::unique_ptr
析构函数需要 的析构函数unique_ptr
,从而实例化它,这需要指向类型的析构函数,它需要该类型要完整。由析构函数的定义触发的这个依赖链必须推迟到指向类型完成时。
在您的引用计数指针的情况下,您想知道指针的复制构造函数、复制赋值运算符和析构函数是否一定需要指向类型的完整性,并且同样它们的实例化被推迟到指向类型完成的时间。
因为指针析构函数可能调用指向类型的析构函数,所以在实例化指针析构函数时该类型必须是完整的。这类似于unique_ptr
,因此应该不足为奇。
类似地,指针复制赋值可能会调用指向类型的析构函数。它用另一个指针替换指针,并减少原始指针的引用计数,并在必要时将其销毁。因此,出于与析构函数相同的原因,指针复制赋值运算符要求指向的类型在实例化时必须是完整的。
复制构造函数更加微妙。不会调用指向类型的潜在破坏。然而,在这个实现中,我们作用于指向类型的基类来增加引用计数。这需要完整性,否则就没有基类。所以,通过这个实现,是的,当复制构造函数被实例化时,指向的类型必须是完整的。
当然,还有其他实现。可以同时保留 aT*
和 a minimal_reference_counter<T>*
,而不是从前者中找到后者作为基类。shared_ptr
做这个。事实上,也可以将析构函数存储为函数指针,而不需要其他笨拙的实例化延迟。shared_ptr
也这样做。
推荐阅读
- python-3.x - ModelCheckpoint - save_best_only=True
- sonarqube - 从 sonarqube 禁用内置规则
- swift - 通过使用 Button 修改变量以便稍后在另一个 View Controller 中使用该变量的最简单方法是什么?
- yii2 - Moonlandsoft/yii2-phpexcel - 编码视图
- spring-boot - 由于异常无法加载 PDF 在提交响应后无法创建会话?
- javascript - 如何使用javascript计算表格的行
- xamarin - 更改 Xamarin Forms 中状态栏的状态栏颜色和文本颜色
- javascript - VueJS 在渲染数据之前等待 Apollo
- vue.js - 如何更新当前页面中的 VUE 组件更改 URL 参数
- ruby - 安装本地构建的 Ruby gem 时不允许安装到父路径