我正试图自己实现一个智能指针。我知道我可以使用智能指针来代替这个,但我尝试这样做只是为了理解智能指针的结构。
问题是,当我的智能指针开始调用析构函数时,这是在检查我的指针是否不是nullptr
,如果是真的,这将删除ptr。
在这之后,当析构函数再次调用CastS时,我得到了一个异常,因为析构函数试图删除一个已经删除的元素,而我的if语句第二次不起作用(正如我所期望的那样),因为删除一个元素后,地址发生了变化,指针不再为空。
如何改进此代码以及如何不删除两次已删除的指针?
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <memory>
using std::cout;
using std::endl;
template<typename T>
class Smart_Pointer
{
private:
T* ptr;
public:
Smart_Pointer(T* ptr);
~Smart_Pointer();
T& operator*();
};
template<typename T>
Smart_Pointer<T>::Smart_Pointer(T* ptr)
{
this->ptr = ptr;
}
template<typename T>
Smart_Pointer<T>::~Smart_Pointer()
{
if (ptr != nullptr)
{
delete ptr;
ptr = nullptr;
}
}
template<typename T>
T& Smart_Pointer<T>::operator*()
{
return *ptr;
}
int main()
{
Smart_Pointer<int> castS(new int(10));
Smart_Pointer<int> castS2 = castS;
}
智能指针是一个总括术语。对于std库,它描述了unique_ptr
、shared_ptr
和weak_ptr
。
如果你想实现一个唯一的ptr,你需要确保只有一个uniqueptr拥有原始指针,因此需要删除复制构造函数和副本赋值运算符。为了能够在唯一指针之间转移所有权,您必须以转移所有权的方式提供移动构造函数和移动赋值运算符。
如果你想实现一个共享ptr,你需要实现引用计数。
如何删除指向同一地址的所有指针?C++
这是你不想做的事情,你想让托管对象保持活动,只要至少有一个共享指针拥有该托管对象。
三/五/零规则:
三规则:如果一个类需要用户定义的析构函数、用户定义的复制构造函数或用户定义的副本赋值运算符,那么几乎可以肯定它需要这三者。
五条规则:由于用户定义的析构函数、复制构造函数或复制赋值运算符的存在阻止了移动构造函数和移动赋值运算符的隐式定义,因此任何需要移动语义的类都必须声明所有五个特殊成员函数:
如何删除指向同一地址的所有指针?C++
您可能无法可靠且自动地做到这一点。请注意Rice定理,并阅读更多关于C++中编程的信息,然后参阅此C++参考资料。要明白,指针将您的虚拟地址空间组织为一些有向图,这些有向图在程序执行过程中不断演变。
也许您想清除指向同一地址的所有指针。
然后,阅读更多关于垃圾回收的内容
例如,阅读GC手册。
考虑在C++源代码上使用一些静态分析工具,例如Clang静态分析器。
还可以考虑生成一些C++代码(如SWIG或GNU bison)。您可以编写C++代码生成器(例如,使用GPP或GNU m4,或者您自己的C++文件生成器),以简化指针的管理。
还可以阅读n3337(一些C++标准草案)和C++编译器的文档(也许是GCC)。
请注意,引用计数有缺点(例如,它对多线程不友好)。
研究现有C++开源程序(如github上)的源代码,如Fish、Qt、RefPerSys、GCC、Clang、ANTLR。考虑为其中一个做出贡献。
如果您试图模拟unique_ptr的行为,只需不允许复制即可解决此问题。如果删除复制构造函数,则没有两个智能指针可以同时指向同一地址。你只需写:
SmartPtr(const SmartPtr&) = delete;
然而,如果你这样做,你可能想要一种转移所有权的方法,所以最好实现一个移动构造函数:
SmartPtr(SmartPtr&& other) {
ptr = std::exchange(other.ptr, nullptr);
}
或者类似的东西。如果你真的想让两个智能指针指向同一个地址,你需要一种方法来决定哪一个要删除ptr,通常是最后一个超出范围的指针。在标准中(例如,在shared_ptr中),这是通过在类的所有实例之间定义一个共享结构来实现的,但这可能远远超出了这个答案的范围。。
如何删除指向同一地址的所有指针?
第一个问题是,您不知道有多少指向同一地址。
这可以通过多种方式解决。
- 使用控制块(如std::shared_ptr),所有智能指针都指向一个控制块,该控制块指向真实对象并具有计数器
- 在指向对象的指针中使用侵入式计数器,要么继承它,要么对象必须由计数器继承
- 使用外部免费计数器
- 分析所有内存,看看是否有任何东西指向该地址(垃圾回收,GC)
如果您打算执行多线程,则计数器应该是原子计数器。
实现一个外部免费计数器有一个巨大的优势,那就是你没有1的间接性。既没有2的侵入性,也没有GC的复杂性。
未测试代码
std::unordered_map<void *, std::atomic_int> extro_count;
然后你需要根据你想要的先进程度来实现3或5的规则
template<typename T>
Smart_Pointer<T>::~Smart_Pointer() {
if (ptr != nullptr) {
if (--extro_count[ptr] == 0) {
extro_count.erase(ptr);
delete ptr;
ptr = nullptr;
}
}
我将其他任务的实施留给作战人员。
这在线程代码中仍然非常不安全,除非您小心。