【问题标题】:Difference between: std::make_unique<char>(size_t size) and std::make_unique<char[]>(size_t size)?之间的区别:std::make_unique<char>(size_t size) 和 std::make_unique<char[]>(size_t size)?
【发布时间】:2021-05-18 07:02:06
【问题描述】:

我正在实现循环数组数据结构,其代码如下所示:

struct CircularArrayException : public std::exception {
    std::string msg;

    CircularArrayException(const std::string arg_msg) 
    : msg{"CircularArrayException: " + arg_msg} {}

    const char * what () const throw () {
        return msg.c_str();
    }
};

template <typename T>
class CircularArray {
public:
    const size_t array_size;
    std::unique_ptr<T> uptr_arr;

    size_t occupied_size = 0;
    int front_idx = -1;
    int back_idx = -1;
 
    CircularArray(const CircularArray& ca) = delete;

    CircularArray& operator=(const CircularArray& ca) = delete;

    CircularArray(
        const size_t arg_array_size
    ):  array_size{arg_array_size} {
        uptr_arr = std::make_unique<T>(array_size);
    };
};

在实现之后,我使用CircularArray&lt;char&gt; 测试了实现,它运行良好。 但是,后来我意识到我们使用std::make_unique&lt;char[]&gt;(num_elements) 来向数组声明一个unique_ptr,而不是std::make_unique&lt;char&gt;(num_elements)。但是,即使那样,代码似乎也能正常工作。我查看了std::make_uniquehere 的文档,无法理解(2)nd 签名的解释。谁能帮我理解其中的区别以及我的代码为何有效?

以下是 cppreference 上 (2) 签名的内容:

template< class T >
unique_ptr<T> make_unique( std::size_t size );

(2) (C++14 起) (仅适用于边界未知的数组类型)

构造一个未知边界 T 的数组。仅当 T 是一个未知边界数组时,此重载才参与重载决议。函数相当于:unique_ptr&lt;T&gt;(new typename std::remove_extent&lt;T&gt;::type[size]())

这里是金螺栓链接:https://godbolt.org/z/K9h3qTeTW

【问题讨论】:

  • 你是在 Valgrind 这样的内存调试器中运行的吗?无论如何,我不认为make_unique() 带有大小参数,因此该参数仅用于初始化创建的对象。只需使用调试器单步调试代码,即可在 cppreference.com 上查找或研究文档。顺便说一句:当您可以在vector 上构建时,我会质疑您选择使用这样的动态分配。考虑在 codereview.stackexchange.com 提交您的代码(一旦它正常工作)。
  • 你的问题的代码太多了!尝试一个最小且易于重现的代码,其中仅包含两个唯一指针:) 这将激励人们更多地阅读和回答您的问题 :)))
  • 感谢 cmets。我会尽量减少这个问题。
  • vector 是一个更好的选择,我会使用它。但是,我仍然想知道make_unique 的这个(2)签名是什么意思。
  • @UlrichEckhardt make_unique 接受 size 参数,如果类型是未知边界的数组。

标签: c++ c++11 c++17 c++14


【解决方案1】:

std::make_unique&lt;char&gt;(65); 创建一个指向 单个 字符的指针,该字符用值 65 ('A') 初始化。 std::make_unique&lt;char[]&gt;(65) 创建一个包含 65 个元素的数组。

如果你运行这段代码:

#include <memory>
#include <iostream>

int main()
{
    auto a = std::make_unique<char>(65);
    std::cout << *a << "\n";
    auto b = std::make_unique<char[]>(65);
    std::cout << (int)b[0] << "\n";
}

由于数组元素未初始化,它将为第一个打印A,为第二个打印一个未定义的值(可能为 0)。

您的代码“工作”是偶然的,使用超过 1 个“数组”元素将导致未定义的行为。

【讨论】:

  • 它工作正常。这是链接:godbolt.org/z/K9h3qTeTW
  • 不,它没有。正如艾伦解释的那样——你正在写你不拥有的内存。它只是碰巧没有崩溃,因为你写了这么短的字符串。如果你写了多个字符,你的代码就会崩溃:godbolt.org/z/e18rGbsPv
  • 所以你的意思是说,在 C++ 中写无主内存是可能的。你不觉得内存不安全吗?
  • @KishoreKaushal C 和 C++ 在设计上以内存不安全着称。
  • 是的,c++ 没有边界检查,如果你只写了一小部分越界,你可能不会覆盖任何重要的东西并且你的代码似乎可以工作,但是再写几个字节,你的代码可能会开始崩溃或更糟糕的是默默地行为不端
猜你喜欢
  • 2020-05-06
  • 2014-04-29
  • 2021-11-07
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 2021-11-16
  • 2021-09-28
相关资源
最近更新 更多