我正在玩C ++和const-correctness。 假设您有以下结构
template <typename T>
struct important_structure {
public:
T* data;
int a;
important_structure(const T& el, int a);
void change();
};
template <typename T>
void important_structure<T>::change() {
//alter data field in some way
}
template <typename T>
important_structure <T>::important_structure(const T& el, int a) : data(&el), a(a) //error line {
};
int main() {
important_structure<int>* s = new important_structure<int>{5, 3};
}
使用std=c++11
进行编译时,编译器会返回以下错误:
从'const int *'无效转换为'int *'
现在,我知道将const int*
转换为int*
是不安全的。问题是我有一个数据结构,我不想把字段data
作为常量。
但是,我不想删除构造函数中的const
限定符,因为我认为它为未来的开发人员提供了信息:它清楚地表明函数不会修改el
。仍然可以通过data
中的其他函数修改字段important_structure
。
我的问题是:如何处理在costructor中初始化并在其他函数中更改的字段? 大多数const正确性处理简单的答案,但毫无疑问(我认为)处理将const参数传递给数据结构然后由其他人更改此类数据结构的情况。
感谢您的回复
答案 0 :(得分:2)
将el
作为const
引用传递并不意味着该函数在函数运行期间不会更改el
,这意味着由于此函数调用,{ {1}}根本没有改变。通过将el
的地址放入非常数el
,您违反了该承诺。
因此,如果您确实想要更改数据,那么干净的解决方案就是删除data
。因为它不为未来的开发人员提供信息,但具有误导性。抛弃const
会非常糟糕。
答案 1 :(得分:1)
让我们使用T
important_struct
类型的简单类:
class Data
{
public:
Data() : something(0){}
Data(int i) : something(i){}
Data(const Data & d) : something(d.something){}
//non-const method: something can be modified
void changeSomething(int s){ something += s; }
//const method: something is read-only
int readSomething() const { return something; }
private:
int something;
};
这个类有一个非常简单但封装良好的状态,即int something
字段,可以通过非常受控制的方式访问。
让({1}}的简化版本将important_structure
的实例作为私有字段保存:
Data
我们可以通过这种方式为template <typename T>
struct important_structure
{
public:
important_structure(T * el);
void change();
int read() const;
private:
T* data;
};
实例分配Data
实例:
important_structure
实例在构造中分配:
important_structure<Data> s(new Data());
现在一个很棒的问题:template <typename T>
important_structure <T>::important_structure(T * el) : data(el) {}
取得所拥有的important_structure
个实体的所有权吗?必须在文档中明确答案。
如果是是,Data
必须处理内存清理,例如像这样的析构函数是必需的:
important_structure
请注意,在这种情况下:
template<typename T>
important_structure<T>::~important_structure()
{
delete data;
}
另一个指向 Data * p = new Data()
// ...
important_structure<Data> s(p);
//p is left around ...
的指针留在周围。如果有人错误地打电话给Data
怎么办?或者,更糟糕的是:
delete
更好的设计会让 Data d;
// ...
important_structure<Data> s(&p); //ouch
拥有自己的 important_structure
实例:
Data
但这可能过于简单或只是不受欢迎。
可以让template <typename T>
struct important_structure
{
public:
important_structure();
void change();
// etc ...
private:
T data; //the instance
};
复制它将拥有的实例:
important_structure
后者是问题中提供的构造函数:传递的对象不会被触及,而是被复制。显然,现在有两个相同的template<typename T>
important_structure<T>::important_structure(const T &el)
{
data = el;
}
个对象。同样,结果可能不是我们首先需要的。
中间有第三种方式:对象在所有者外部实例化,移动,使用move semantics。
举个例子,让我们给Data
一个移动赋值运算符:
Data
让Data & operator=(Data && d)
{
this->something = d.something;
d.something = 0;
return *this;
}
提供一个接受important_structure
的右值引用的构造函数:
T
仍然可以使用临时作为必需的右值传递important_structure(T && el)
{
data = std::move(el);
}
实例:
Data
或现有的,提供左值所需的参考,感谢std::move:
important_structure<Data> s(Data(42));
在第二个示例中,Data d(42);
// ...
important_structure<Data> x(std::move(d));
std::cout << "X: " << x.read() << std::endl;
std::cout << "D: " << d.readSomething() << std::endl;
所持有的副本被视为好的,而另一个被保留为处于有效但未指定的状态,只是为了遵循标准的图书馆习惯。
这种模式,恕我直言,在代码中更清楚地说明,特别是如果认为这段代码不能编译:
important_structure
如果想要Data d(42);
important_structure<Data> x (d);
的实例,则必须提供临时important_structure
个实例,或明确移动Data
的现有实例。
现在,让std::move
类成为容器,正如您在评论中提到的那样,以便{GH}可以从外部访问important_structure
。让我们为data
类提供这样的方法:
important_structure
现在,我们可以使用const T & owneddata() { return data; }
这样的const方法:
data
但是要求`数据&#39;非const方法将无法编译:
important_structure<Data> s(Data(42));
std::cout << s.owneddata().readSomething() << std::endl;
如果需要它(希望不是),请公开一个非const引用:
s.owneddata().changeSomething(1000); //not compiling ...
现在T & writablereference() { return data; }
字段已完全处理:
data
答案 2 :(得分:0)
使用data(&el)
和new important_structure<int>{5, 3};
是一个非常糟糕的主意,因为它暗示您可以写:
new important_structure<int>{5, 3};
但是编写data
会导致数据在调用构造函数后持有一个不再有效的地址。
如果您希望更改点value
,但指针所指向的template <typename T>
struct important_structure {
public:
T const * data;
int a;
important_structure(T const * el, int a);
void change();
};
template <typename T>
void important_structure<T>::change() {
//alter data field in some way
}
template <typename T>
important_structure <T>::important_structure( T const * el, int a) : data(el), a(a) { //error line
};
int main() {
int i = 5;
important_structure<int>* s = new important_structure<int>{&i, 3};
}
无法更改,那么您希望以这样的方式编写它:
SELECT
COLUMN_NAME
FROM
information_schema.COLUMNS
WHERE
TABLE_NAME = 'city';