使用make_shared通过shared_ptr包装动态数组

rob*_*ert 6 c++ arrays smart-pointers shared-ptr

我想给数组写一些字节.为了利用现代C++,我决定使用智能指针.

#include <memory>
#include <cstdint>

using namespace std;

void writeUint32_t(uint32_t value, unsigned char* p){
    *p     = static_cast<unsigned char>((value >> 24) & 0xFF);
    *(++p) = static_cast<unsigned char>((value >> 16) & 0xFF);
    *(++p) = static_cast<unsigned char>((value >>  8) & 0xFF);
    *(++p) = static_cast<unsigned char>((value      ) & 0xFF);
}

int main(){
    auto buf = make_shared<unsigned char[]>(512);
    uint32_t data = 1234;
    writeUint32_t(data, buf.get() + 8);
}
Run Code Online (Sandbox Code Playgroud)

但是,我收到以下编译错误:

u.cpp:15:37: error: invalid use of array with unspecified bounds
 writeUint32_t(data, buf.get() + 8);
                                 ^
u.cpp:15:38: error: cannot convert ‘unsigned char (*)[]’ to ‘unsigned char*’ for argument ‘2’ to ‘void writeUint32_t(uint32_t, unsigned char*)’
 writeUint32_t(data, buf.get() + 8);
Run Code Online (Sandbox Code Playgroud)

我在使用g++ (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609 是否有办法在这种情况下使用智能指针?

son*_*yao 10

不要使用std::make_shared原始数组,它不会像你期望的那样构造一个数组,但会尝试创建一个指向你指定类型的指针,即unsigned char[].正如错误消息所说,这就是你unsigned char (*)[]何时使用的原因get().

并且默认情况下std::shared_ptr将删除指针delete,而不是delete[]应该用于数组.您需要为其指定自定义删除器,但std::make_shared不允许您指定它.

你可以(1)std::shared_ptr直接初始化并指定删除器,如

std::shared_ptr<unsigned char> buf(new unsigned char[512], [](unsigned char* p)
{
    delete[] p;
}); 
Run Code Online (Sandbox Code Playgroud)

(2)std::unique_ptr改为使用,它为数组提供了指定的版本,包括delete[]解除分配和提供时的调用operator[](std::shared_ptr将从C++ 17支持它).

auto buf = std::make_unique<unsigned char[]>(512);
Run Code Online (Sandbox Code Playgroud)

(3)考虑std::vector<unsigned char>std::array<unsigned char>.

  • `std::vector&lt;unsigned char&gt;` 添加了另一级间接寻址,并且 `std::array&lt;unsigned char&gt;` 要求在编译时知道大小。两者都不等同于所需的“std::shared_ptr&lt;char[]&gt;”。 (2认同)

Sme*_*eey 6

如果您真的想为此使用shared_ptr带有数组的 a(而不是 StoryTeller 建议的向量),您的类型应该是unsigned char而不是unsigned char[]. 为了确保正确删除数组,您需要指定一个数组删除器以传递给shared_ptr构造函数(因此您不能使用,make_shared因为这不允许您指定删除器):

auto buf = std::shared_ptr<unsigned char>(new unsigned char[512], std::default_delete<unsigned char[]>());
Run Code Online (Sandbox Code Playgroud)


Sto*_*ica 5

我建议你使用std::vector<unsigned char> vec(512);,包装连续的动态数组正是它的用途.获取原始缓冲区指针非常简单vec.data();

如果需要共享向量,则仍然可以使用智能指针

auto p_vec = make_shared<vector<unsigned char>>(512);
Run Code Online (Sandbox Code Playgroud)

你会几乎没有开销获得引用计数的好处,由于采用矢量,并且你会得到整个向量API.

  • 除非您不想要 **整个向量 API**,例如您明确不希望有人从缓冲区中推送或弹出;除了附在上面的尺寸之外,你实际上不需要任何东西。 (2认同)

Bia*_*sta 5

您不能使用:

std::make_shared<unsigned char[]>(512);
Run Code Online (Sandbox Code Playgroud)

这是因为没有专门针对Type[]. 那是因为实际上还不支持。它将在新标准C++17中:std::shared_ptr<T> operator[]

运算符[] (C++17)


解决方案可能是使用STL 容器,或者 IMO 更好:std::unique_ptr

确实,std::unique_ptr支持operator[]

从 C++14 开始,您可以:

auto buffer = std::make_unique<unsigned char[]>(512);
buffer[index];
Run Code Online (Sandbox Code Playgroud)

它是异常安全的,几乎零开销,并且可以用作数组缓冲区。

此外,缓冲区的释放是通过正确的运算符调用来正确处理的delete[]