Это, возможно, один из случаев, когда имеет смысл создать собственный класс для управления памятью (без бизнес-логики, только память).
Вы, конечно, можете сформулировать этот класс вокруг векторов:
#define CHECK_CLASS_INVARIANT() \
ClassInvariant inv##__LINE__ (*this); (void) inv##__LINE__;
class Foo {
public:
size_t size() const { return _a.size(); }
float* getA() { return &_a[0]; }
float* getB() { return &_b[0]; }
void push(float a, float b) {
CHECK_CLASS_INVARIANT()
_a.push_back(a);
_b.push_back(b);
}
private:
struct ClassInvariant {
ClassInvariant (Foo& f): _f(f) {}
~ClassInvariant () { assert(_f._a.size() == _f._b.size()); }
Foo& _f;
}; // struct ClassInvariant
std::vector<float> _a;
std::vector<float> _b;
};
Мы всегда проверяем инвариант класса (в методах мутирования), чтобы убедиться, что он всегда применяется.
Примечание: здесь небольшой поток, если_b.push_back(b);
throws, затем _a
был расширен, а _b
- нет, это может быть обработано в блоке try / catch ИЛИ, как метко заметил SteveJessop, (1) предварительно зарезервировав достаточно места в обоих векторах и (2) pushв них.