我有以下代码,这些代码似乎总是有效的(msvc、gcc和clang(。
但我不确定这是否真的合法。在我的框架中,我的类可能有";两个构造函数"-一个进行简单成员初始化的普通C++构造函数和一个附加的成员函数";Ctor";其执行附加的初始化代码。例如,它用于允许调用虚拟函数。这些调用由一个通用的分配/构造函数处理;make_shared";。
代码:
#include <iostream>
class Foo
{
public:
constexpr Foo() : someConstField(){}
public:
inline void Ctor(int i)
{
//use Ctor as real constructor to allow for example calls to virtual functions
const_cast<int&>(this->someConstField) = i;
}
public:
const int someConstField;
};
int main()
{
//done by a generic allocation function
Foo f;
f.Ctor(12); //after this call someConstField is really const!
//
std::cout << f.someConstField;
}
修改const
内存是未定义的行为。这里,默认构造函数已经在常量内存中分配了int
。
老实说,我不知道你为什么要这么做。如果你想用int
初始化Foo
,只需创建一个重载构造函数:
...
constexpr Foo(int i) : someConstField{i} {}
这是完全合法的,当创建const
内存时,您正在初始化它,一切都很好。
如果出于某种原因,您希望在两个阶段初始化对象(如果没有工厂函数,不是是个好主意(,那么您不能也不应该使用const
成员变量。毕竟,如果它在对象创建后可以更改,那么它将不再是常量。
根据经验,不应该有const
成员变量,因为它会导致很多问题,例如移动对象。
当我说";常量存储器";这里,我指的是const
语言规则限定的内存。因此,虽然内存本身在机器级可能是可写的,也可能不是可写的。但这并不重要,因为编译器会做它喜欢做的任何事情(通常它只是忽略对内存的任何写入,但这是UB,所以它可以做任何事情(。
否。
修改const
值是未定义的行为。const_cast
本身很好,问题出在改装上。
根据C++17标准中的7.1.6.1
除了任何声明为可变的类成员(7.1.1(都可以修改外,任何修改常量的尝试对象在其生存期内(3.8(会导致未定义的行为。
还有一个例子(与您的例子类似,只是不适用于类成员(:
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
如果分配函数分配原始内存,则可以使用placementnew
在该内存位置构造对象。这样,您必须记住在释放分配之前调用对象的析构函数。
使用malloc
:的小示例
class Foo
{
public:
constexpr Foo(int i) : someConstField(i){}
public:
const int someConstField;
};
int main()
{
void *raw_memory = std::malloc(sizeof(Foo));
Foo *foo = new (raw_memory) Foo{3}; // foo->someConstField == 3
// ...
foo->~Foo();
std::free(foo);
}
Ctor
之后,someConstField
的值将保持常量。只要在构造函数中设置它,就不会有任何问题,代码也会变得更可读。
#include <iostream>
class Foo
{
public:
constexpr Foo(int i) : someConstField(Ctor(i)){}
int Ctor(); // to be defined in the implementation
const int someConstField;
};
int main()
{
Foo f(12);
std::cout << f.someConstField;
}