让我们假设我有一个带有私有std :: vector成员的类,并且该类对vector的内容保持某种约定,例如,使元素保持排序。
#include <algorithm>
#include <utility>
#include <vector>
class Foo
{
public:
using value_type = std::pair<int, float>;
Foo() = default;
explicit Foo(const std::vector<value_type>& data):
data_(data)
{
std::stable_sort(data_.begin(), data_.end(), [](const value_type& lhs, const value_type& rhs)
{
return lhs.first < rhs.first;
});
}
explicit Foo(std::vector<value_type>&& data):
data_(std::move(data))
{
std::stable_sort(data_.begin(), data_.end(), [](const value_type& lhs, const value_type& rhs)
{
return lhs.first < rhs.first;
});
}
// Return the element corresponding to the given key,
// 0 if there is no such element
float getValue(int key) const
{
auto cmp = [](const value_type& element, int key)
{
return element.first < key;
};
auto it = std::lower_bound(data_.begin(), data_.end(), key, cmp);
if (it == data_.end() || it->first != key)
{
return 0;
}
return it->second;
}
// Increment all keys by the specified value
void incrementAllKeys(int value)
{
for (value_type& element : data_)
{
element.first += value;
}
}
// Add an element.
// Internal vector remains sorted.
// O(N) complexity in the worst case (adding an element to front),
// because data has to be moved.
void addElement(int index, float value);
private:
std::vector<value_type> data_;
};
所有成员函数都维护类协定(data_已排序)。但是,用户仍然可以 将数据移至Foo时违反合同:
std::vector<std::pair<int, float>> v
{
{10, 1.4322f},
{1, 3.223f},
{5, 2.2323}
};
// Get a pointer to actual data
std::pair<int, float>* ptr = v.data();
Foo foo {std::move(data)}; // OK, the constructor sorts data
// Legal: ptr is not invalidated after move/sort
assert(ptr[0].first == 1);
// Break the internal state
ptr[0].first = 42;
我想确保用户无法修改Foo :: data_。当然,我可以简单地禁止将数据移动到Foo中,但是由于性能(冗余复制),这是不希望的。
此问题不仅限于std :: vector:这适用于在移动后指针/引用和/或迭代器不会失效的任何容器。
我考虑使用Builder模式,例如:
class FooBuilder
{
public:
void reserve(std::size_t capacity);
void addElement(int key, float value);
// Moves data into Foo.
// This class has to be a friend of Foo;
// alternatively, Foo can be constructible from FooBuilder and FooBuilder should have some
// std::vector<std::pair<int, float>> FooBuilder::release();
// member function.
Foo build();
private:
std::vector<std::pair<int, float>> data_;
};
FooBuilder解决了公开问题,同时允许移动语义,但与std :: vector相比,其公共接口非常有限。
有更好的方法吗?