粗略地说,我有一个包含const unsigned char数组的类。这个类的对象是由一个特殊的工厂函数创建的,该函数还负责构造数组(在堆上)。当在工厂函数中创建一个对象时,它将被赋予指向数组的指针。数组不会被复制,对象只会使用给定的指针。在销毁时,它将释放数组占用的内存块。
class Foo
{
private:
const unsigned char* array;
size_t size;
public:
Foo(const unsigned char* array, size_t size) : array(array), size(size) {}
~Foo() {delete [] array;}
};
Foo* factoryFunction(const void* data, size_t size)
{
unsigned char* array = new unsigned char[size];
memcpy(array, data, size);
return new Foo(array, size);
}现在我想知道是否有任何副作用,因为new []返回unsigned char *,而delete []是为const unsigned char *调用的。不过,我没有得到任何分割错误。
发布于 2013-04-19 16:27:20
这很好,标准中的非标准化文本表明了同样的事情:
[C++11: 5.3.5/2]:如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并在本节的其余部分中使用转换后的操作数替换原始操作数。在第一个备选方案(删除对象)中,delete的操作数的值可以是空指针值、指向由先前的new表达式创建的非数组对象的指针、或者指向表示此类对象的基类的子对象(1.8)的指针(第10条)。如果不是,则行为未定义。在第二种选择(删除数组删除delete)中,删除数组的操作数的值可以是空指针值,也可以是由前一个数组删除数组产生的指针值。如果不是,则行为未定义。[注意:这意味着delete-expression的语法必须与new分配的对象类型匹配,而不是与new-expression的语法匹配。-end delete-expression;] [注意:指向常量类型的指针可以是常量类型的操作数。在将指针表达式用作 const -end注意事项的操作数之前,不必丢弃该表达式的常量(5.2.11) ]
以下几段可能会引起争议:
[C++11: 5.3.5/3]:在第一种选择(删除对象)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类,并且静态类型应具有虚拟析构函数或行为未定义。在第二种选择中删除(数组)如果要删除的对象的动态类型与其静态类型不同,则行为未定义。
虽然这篇文章的意图在我看来只是处理多态性,但结合下面的文章,它可能被解释为,严格地说,你的代码实际上调用了未定义的行为:
[C++11: 3.9.3/1]:..类型的cv限定或cv非限定版本是不同的类型;..
然而,我有理由相信,这可以被视为标准中的措辞缺陷;其意图对我来说似乎很清楚。
同样,这条规则甚至可能暗示程序不会编译:
[C++11: 12.5.4]:..如果删除表达式以一元::运算符开头,则在全局范围内查找释放函数的名称。否则,如果删除表达式用于释放其静态类型具有虚拟析构函数的类对象,则释放函数是在动态类型的虚拟析构函数的definition点选择的函数(12.4)。否则,如果delete表达式用于释放类T或其数组的对象,则对象的静态和动态类型应该是相同的删除,并且在T的作用域中查找释放函数的名称。如果此查找失败,则在全局作用域中查找该名称。如果查找的结果不明确或不可访问,或者如果查找选择了一个位置释放函数,则程序是病态的。
但是,这不是规则的目的,它解决了在没有虚拟析构函数的情况下的多态性,以及跨多个类声明的真正的多义性。
总而言之,在解释这些规则的措辞时,您最好的选择仍然是我们开始时的非规范但非常明确的注释。
发布于 2013-04-19 16:13:21
应该不会有任何问题,因为"unsigned char“(或任何数据类型)上的"const"-ness使其成为只读数据。
在delete中使用"[]“指示必须删除一个数组。
要了解"delete“和"delete []”背后的含义,请阅读this。
发布于 2013-04-19 17:01:47
首先,您应该避免在c++中使用这种类型的数组,而应该使用std::vector<unsigned char>。
关于const关键字。它只是告诉编译器和读取代码的人,这个成员/变量/参数不应该被更改。
使用指针时,const关键字的位置很重要:
(编辑:找到我借用这部分的Greyson的答案)
const关键字将其左侧的部分标记为常量(如果它位于开头,则用于紧随其后的类型,因此const unsigned char和unsigned char const相等)
要将指针标记为常量,请执行以下操作(内容仍可更改):
unsigned char * const aConstantPointerToAMutableContent;要将内容标记为const,请执行以下操作:
const unsigned char * aPointerToAConstantContentA;
unsigned char const * aPointerToAConstantContentB;要将两者标记为常量,请执行以下操作:
const unsigned char * const aConstantPointerToAConstantContent;它对编译器和用户都是一个提示,这样就可以清楚地知道对数据做了什么或没有做什么。如果参数为const unsigned char *,那么用户将知道如果传递该内容,该内容将不会被更改。
因为const关键字只是一个标记,如果某些东西是可变的,那么它对大小本身没有影响,所以对于delete,它应该没有影响。(请参阅Lightness Races in Orbit的答案和评论,也可以将"Is const_cast safe?“作为兴趣)。
但是我不喜欢你的代码的是构造函数是公共的。我可以直接调用它,但因为参数是const unsigned char* array,所以我不希望该类更改内容或在销毁时将其删除。(我不期望直接创建对象的行为方式与使用工厂的方式不同。)
因此,我会将工厂方法作为类的static方法,并将构造函数设置为protected。
https://stackoverflow.com/questions/16100069
复制相似问题