不安全的模板数组构造函数

时间:2016-09-07 15:10:41

标签: c++

这可能是一个简单的问题,但我有template class

template<typename Type>
class Array {
    size_t n;
    Type* buff;
public:
    Array(size_t n_): n(n_), buff(new Type[n]) {}
};

代码来自课程pdf文件,其中buff(new Type[n])不安全。我不明白为什么它不安全,是不是size_t一般都没有签名?我可以举例说明它可能有编译和/或运行时错误吗?

5 个答案:

答案 0 :(得分:12)

代码是&#34;不安全&#34;因为它依赖于n之前构建的buff。这种依赖性会增加代码的脆弱性。

当你构造类的成员时,它们是按照在类中声明的顺序构造的,而不是在成员初始化列表中如何调用它们,所以如果代码被更改为

template<typename Type>
class Array {
    Type* buff;
    size_t n;
public:
    Array(size_t n_): n(n_), buff(new Type[n]) {}
};

然后,当您执行buff(new Type[n])时,n未初始化且您有未定义的行为。

答案 1 :(得分:3)

首先,顺序,执行构造函数的初始化不是由它们写下来的顺序决定的,而是由初始化字段出现在代码中的顺序决定:

class Array {
    size_t n;
    Type* buff;
public:
    Array(size_t n_): n(n_), buff(new Type[n]) {}
};

这里首先将n初始化然后buff。

class Array {
    Type* buff;
    size_t n;
public:
    Array(size_t n_): n(n_), buff(new Type[n]) {}
};

现在,第一个buff将被初始化,然后是n,因此在这种情况下,n没有定义的值。

使用构造函数的初始化列表是很好的做法,但要小心,不要在订单上创建任何假设。

一般来说,不要拥有原始指针是个好主意。如果您使用智能指针,则不能忘记发布数据。

在特定情况下,您可能还想使用std :: vector而不是C样式数组。它以线程安全的方式为您处理所有分配,重新分配,发布等。看起来你正在尝试编写类似于你自己的std :: vector的东西。请仅为教育目的这样做。始终更喜欢生产代码中的标准实现。很长一段时间你可能不会让它变得更好。如果你这样做,你会在这里提出不同的问题; - )

答案 2 :(得分:3)

首先,你有内存泄漏。但问题可能与此无关。所以我们假设您有一个析构函数来释放数组。

template<typename Type>
class Array {
    size_t n;
    Type* buff;
public:
    Array(size_t n_): n(n_), buff(new Type[n]) {}
    ~Array() { delete[] buff; }
};

现在特定代码非常安全。分配n_时不会抛出异常,初始化顺序正确,buff是类中唯一的原始指针。但是,当您开始扩展类并编写更多类时,内存泄漏的风险会增加。

想象一下,您需要再向class Array添加一个成员:

template<typename Type>
class Array {
    size_t n;
    Type* buff;
    SomethingElse xyz;
public:
    Array(size_t n_): n(n_), buff(new Type[n_]), xyz(n_) {}
    ~Array() { delete[] buff; }
};

如果SomethingElse的构造函数抛出,则为buff分配的内存将泄漏,因为将永远不会调用析构函数~Array()

Modern C ++调用诸如Type* buff 原始指针之类的指针,因为您自己负责释放存储(考虑异常),并引入std::unique_ptr和{{}等工具。 {3}}可以自动处理存储释放。

在现代C ++中,你可以像这样编写你的类:

template<typename Type>
class Array {
    size_t n;
    std::unique_ptr<Type[]> buff;
public:
    Array(size_t n_): n(n_), buff(new Type[n_]) {}
};

注意没有析构函数。 unique_ptr将负责为您调用delete

请注意,初始化列表中的类成员也不依赖(只需编写new Type[n_]而不是new Type[n],这样可以使代码更加健壮)

答案 3 :(得分:0)

C ++ 98 Standard 12.6.2.5.4(我不希望新版本放宽这个。)

  

- 然后,非静态数据成员应按其顺序初始化   在类定义中声明(再次无论顺序如何)   记忆初始化者。)

因此,初始化顺序是根据这个定义的。

如果您想要一个如何崩溃的示例,只需制作sizeof(Type)*n&gt;系统中的总内存。

答案 4 :(得分:-1)

在初始化列表中调用new运算符是不安全的。 如果new失败,则不会调用Array的析构函数。 这是一个类似的问题。 Are there any issues with allocating memory within constructor initialization lists?

相关问题