简体   繁体   中英

C++ pre-allocated vector of objects containing pointer

I have the following structure:

struct CacheNode {
  set<int> *value;
  int timestamp;
  CacheNode() : value(new set<int>()), timestamp(0) {}
};

And I pre-allocate a vector of them as follows:

vector<CacheNode> V(10);

When I do this, every CacheNode element in the vector points to the same set<int> in its value field. In particular,

  V[0].value->insert(0);
  cout << V[1].value->size() << endl;

prints out 1 instead of the 0 that I want.

What is the correct way to pre-allocate the vector (or to declare the structure) so that each CacheNode have its own set<int> instance?

(Note: I do need the value to be a pointer to a set, because it is possible in my application for some CacheNodes to share sets.)

vector<CacheNode> V(10); creates an initial CacheNode object and then copies it 10 times. So you have 10 identical objects.

You can use generate_n :

std::vector<CacheNode> v;
std::generate_n(std::back_inserter(v), 10u, [](){ return CacheNode{}; });

Here's an example program .

You have violated the rule of 3. You have created an object with a non-trivial constructor, and failed to create a destructor or copy constructor or operator= .

std::vector<blah> foo(10) creates a single default constructed blah , and makes 10 copies of it in foo . Because you violated the rule of 3, these 10 copies are all identical.

The easiest method would be to do away with the new :

struct CacheNode {
  std::set<int> value;
  int timestamp;
  CacheNode() : value(), timestamp(0) {}
};

another route would be to use a unique_ptr for lifetime management, and explicitly copy:

struct CacheNode {
  std::unique_ptr<std::set<int>> value;
  int timestamp;
  CacheNode() : value(new std::set<int>()), timestamp(0) {}
  CacheNode(CacheNode&&) = default; // C++11 feature
  CacheNode(CacheNode const& other):value(new std::set<int>( *other.value ) ), timestampe(other.timestamp) {}

  CacheNode& operator=(CacheNode const& other) {
    value.reset(new std::set<int>(*other.value));
    timestampe = other.timestamp;
    return *this;
  }
  CacheNode& operator=(CacheNode&& other) = default;
  // no need for ~CacheNode, unique_ptr handles it
};

when you want to take the std::set<int> out of your CacheNode , call CacheNode().value.release() and store the resulting std::set<int>* .

std::shared_ptr<std::set<int>> would allow shared ownership of the std::set .

There are other approaches, including making your vector store pointers to CacheNode , creating value_ptr<T> templates that do value semantics, etc.

In C++11, these are relatively easy and safe, because std::vector will move things around, and move semantics on a value_ptr<T> won't create a new T .

I am a bit leery of your plan to have shared std::set<int> between different CacheNode , because in general that is bad smell -- the ownership/lifetime of things should be clear, and in this case you have some CacheNode that own the std::set<int> and others that don't (because they share ownership). A shared_ptr can get around this, but often there are better solutions.

You will want to use vector.assign(10, CacheNode()) as what your doing is a way of reserving space mostly.

Also you should do what the other ones say, provide virtual destructor and such.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM