Returning Pointer Vector - Understanding

I am trying to understand the following (lets pretend that MyStorageClass is huge):

class MyStorageClass { public: string x; string y; string z; }; class storage { public: storage(); ~storage() { vector<MyStorageClass *>::iterator it = myVec.begin(); it != myVec.end(); it++) { delete *it; } vector<MyStorageClass*> getItems() { for(int i = 0; i < 10; ++i) { v.push_back(new MyStorageClass()); } return v; } private: vector<MyStorageClass*> v; }; main() { storage s; vector<MyStorageClass*> vm = s.getItems(); } 

From my understanding, when s returns a vector and is assigned vm , this is done as a copy (by value). Therefore, if s out of scope and calls it a destructor, vm has its own copy, and its structure is not affected. However, the transition by value is ineffective. So, if you change it to pass by reference:

 vector<MyStorageClass*>& getItems() { for(int i = 0; i < 10; ++i) { v.push_back(new MyStorageClass()); } return v; } 

You are passing memory location v (in the Storage class). But you still assign a copy of it using the = operator to the vm vector in the Main class. Thus, vm is independent of v , and if the Storage destructor is called vm unaffected.

Finally, if getItems returned the link and basically you had the following:

 main() { storage s; vector<MyStorageClass*> &vm = s.getItems(); } 

Now vm contains the address v . It is affected by the storage destructor.

Question: Have I already said above?

+4
source share
5 answers

Yes, you got it right. Now, if you want to go to the next level, start by looking for reasons why this is bad:

  • The returned pointer or references to the internal elements of the class break the encapsulation.
  • The link you receive will become a sagging link after destroying the corresponding object s , thereby violating the invariant that the links are always valid.
  • By returning the vector of pointers, you leave the caller interested in the weather, he must delete these pointers or not.

The best storage solution is to expose the begin and end methods that return the corresponding iterators from s . Alternatively, you can use Visitor -pattern for algorithms that should work on s .

In addition, in your case, it seems that the vector s should own the objects contained in it. This would be a good indicator of using boost::ptr_vector .

+7
source

I think you stated that this is true, but I am a little confused for this purpose.

 vector<MyStorageClass*> &vm = s.getItems(); 

gets the vector by reference. But the vector contains pointers, so a vector that goes out of scope will not lead to the fact that destructors will be launched in the first place - destructors will only be executed if they are smart pointers of some type (and even then it depends) .

Thus, you can happily pass a vector of pointers around a value without a significant amount of problems. I’m sure that you will save some efficiency by linking it, but I don’t think it is as serious as you think.

In addition, your objects with a pointer to objects are created using a new one (dynamically), so you can return a vector by value in the same way, without fear of losing objects that point to objects.

So, again, I think your logic is fine and your link path is a bit more efficient, but I just wanted to make sure you knew that it would work in both directions without problems :) (and since its pointer vector, by value is also not so bad).

PS: in terms of links, you need to worry about chatter, which can be more unpleasant than you think, as mentioned above in Bjorn. If you have ever used string.c_str (), you may have received this. If you get .c_str () from a string, and the original string goes out of scope, the pointer returned by .c_str () hangs (points to memory that is no longer used for this purpose), and access to it leads to undefined behavior. So, by value, there is likely to be a better option (but it depends on your design - for example, if it is a singleton that lasts throughout your application, freezing is probably not a problem).

0
source

Despite the fact that the vector copies its values, the values ​​in your case are pointers, not the objects that they point to. Thus, " vm has its own copy" is not completely true: the resulting vector in your first code fragment will have a copy of pointers but not the MyStorageClass objects that they point to; therefore, in virtually all of your three code examples, pointers stored in vm will no longer be valid if the storage destructor is called!

If you need to make sure, however, that Storage will not be destroyed until the last access to one of the MyStorageClass objects in any case, then of the methods presented, the third option will be preferable, since vector data (i.e. pointers) are stored only once in memory. You should consider returning the const vector, however, otherwise each calling getItems object can modify the v vector of the Storage class through the returned link.

As others have already pointed out, exposing the vector itself may not be in favor of the idea in the first place; you might consider using iterators instead of a visitor pattern.

In addition, the use of pointers - where not absolutely necessary - is used in large projects that are usually not approved. Consider using smart pointers such as std::auto_ptr (not very recommended, though due to weird copy semantics), or the more easily understood boost::shared_ptr / std::tr1::shared_ptr .

As a note, the first and second code examples (at least in most modern compilers) have the same performance, since in the first case the compiler can optimize the temporary return value (check "Return" value optimization ").

0
source

Remember that your storage class will cause problems if class storage objects are copied. Since you are not creating a copy constructor or assignment operator, the default value will be used. By default, it will blindly copy the vector of pointers, and now you have two storage objects that will try to delete pointers in the vector.

0
source

From my understanding, when s returns a vector and is assigned vm , this is done as a copy (by value). Therefore, if s out of scope and calls it a destructor, vm has its own copy, and its structure is not affected.

Simple pointers ( T* ) should not have the std::vector copy constructor or assignment operator (although you can use the smart pointer class that will copy). Since pointers indicate (“pointed”) are not copied, the copy operation is a shallow copy. While operations with sv will not affect vm (and vice versa), everything that affects pointed access from one will affect the other, since they are the same.

If you were to save a properly implemented smart pointer to sv , not MyStorageClass* , the standard std::vector copy operations could make deep copies so that the contents of sv can be changed without affecting the contents of vm any way (and vice versa ) Copy pointer copy operations will use copy operations of the specified class to duplicate a pointed object. As a result, only one copy pointer will be pointed at each sharp object.

Alternatively (as mentioned in other publications), you can use a smart pointer that allows shared ownership and allows you to manage specified objects by eliminating the delete call in ~storage .

However, transmission by value is inefficient.

However, in some cases this is correct, especially if the mutation of one instance of the container should not affect another (the case you mentioned), in which case you cannot leave without a copy. Correct trump cards are effective. Generally speaking, you can use the copy-replace idiom to reduce inefficiencies due to the creation of time series. As far as I know, most STL implementations do not use copy-swap for std::vector . Copy-on-write , which will only copy when the vector changes, can also help. Threading complicates copy-on-write and can lead to inefficiencies.

Now vm contains the address v .

vm does not contain an address. Although links may use pointers under the hood (they may also not be, may not even require additional storage, in accordance with § 8.3.3-3 C ++ 03), at the language level, links are not pointers. Note that you can have null pointers, but not null references. A more accurate instruction is that vm smoothed to v ( vm and v refer to the same object), so operations on v will affect vm (and vice versa).

0
source

Source: https://habr.com/ru/post/1384116/


All Articles