Is it possible to return a reference to a member variable of a non-pointer as a pointer?

I recently met some C ++ code that looked like this:

class SomeObject { private: // NOT a pointer BigObject foobar; public: BigObject * getFoobar() const { return &foobar; } }; 

I asked the programmer why he did not just make the foobar a pointer, and he said that in this way he did not need to worry about allocating / freeing memory. I asked if he considered using some kind of smart pointer, he said that it worked just as well.

Is this a bad practice? It seems very hacky.

+4
source share
6 answers

Is this a bad practice? It seems very hacky.

It. If the class goes out of scope before the pointer, the member variable will no longer exist, but the pointer to it still exists. Any attempt to dereference the destruction of the post-pointer class will result in undefined behavior β€” this can lead to failure or can make it difficult to find errors when arbitrary memory is read and processed as a BigObject.

if he considers using some smart pointer

Using smart pointers, in particular std::shared_ptr<T> or a forced version, will technically work here and avoid potential failure (if you select it through the general pointer constructor), but it also confuses who the pointer belongs to - the class or the caller? Also, I'm not sure if you can just add an object pointer to a smart pointer.

Both of these points relate to the technical problem of getting a pointer from a class, but the real question should be β€œwhy?”. like in "why are you returning a pointer from a class?" There are times when this is the only way, but more often than not you need to return a pointer. For example, suppose a variable is to be passed to the C API, which takes a pointer to this type. In this case, you are probably better encapsulating the C call in the class.

+1
source

This is perfectly reasonable, and not "hacking" in any way; although we can assume that it is better to return the link to indicate that the object definitely exists. The pointer may be empty and may cause some to think that they should delete it after use.

An object must exist somewhere, and existing as a member of the object is usually as good as anywhere else. Adding an additional level of indirection by dynamically highlighting it separately from the object that belongs to it makes the code less efficient and puts a strain on making sure that it is correctly freed.

Of course, a member function cannot be const if it returns a non-t20> reference or a pointer to an element. This is another advantage of making it a member: a const qualifier in SomeObject also applies to its members, but does not apply to any objects in which it simply has a pointer to.

The only danger is that the object may be destroyed, and someone else has a pointer or a link to it; but this danger is still present, but you control it. Smart pointers can help here if the lifetime of an object is too complicated to manage otherwise.

+6
source

You are returning a pointer to a member variable, not a link. This is a bad design. Your class controls the lifetime of the foobar, and by returning a pointer to its members, you allow consumers in your class to continue to use the pointer outside the lifetime of SomeObject . It also allows users to change the state of the SomeObject as they wish.

Instead, you should reorganize your class to include the operations that will be performed in foobar in the SomeObject class, as methods.

ps. Consider naming your classes correctly. When you define this class. When you instantiate, you have an object of this class.

+3
source

It is generally considered less than ideal for returning pointers to internal data; this prevents the class from controlling access to its own data. But if you want to do this, I do not see a big problem here; this simplifies memory management.

+2
source

As long as the caller knows that the pointer returned with getFoobar() becomes invalid when SomeObject destroyed, this is normal. Such caveats and caveats are common in older C ++ programs and frameworks.

Even current libraries should do this for historical reasons. for example std::string::c_str , which returns a pointer to an internal buffer in a string that becomes unusable when the string is destroyed.

Of course, this is difficult to provide in a large or complex program. In modern C ++, the preferred approach is to simplify the "semantics of values" as much as possible, so that every lifespan of an object is controlled by code that uses it trivially. Thus, there are no bare pointers, without explicit calls to new or delete scattered across your code, etc., and therefore there is no need to require programmers to manually make sure that they follow the rules.

(And then you can resort to smart pointers in those cases when you cannot completely avoid sharing responsibility for the lifetime of an object.)

+1
source

Two unrelated questions:

1) How do you like your SomeObject instance to manage the BigObject instance it needs? If each instance of SomeObject needs its own BigObject , then the BigObject data element is quite reasonable. There are situations when you want to do something else, but if this situation does not arise, stick to a simple solution.

2) Do you want to give SomeObject users direct access to its BigObject ? By default, the answer here is no, based on good encapsulation. But if you want it, then this will not change the estimate (1). Also, if you want it, you do not need to do this with a pointer - it can be through a link or even a public data element.

A third possible problem may arise that changes the score (1):

3) Do you want to give SomeObject users direct access to the BigObject instance that they continue to use beyond the life of the SomeObject instance from which they got it? If so, then of course the data member is not good. The correct solution could be shared_ptr , or for SomeObject::getFooBar should be a factory that returns a BigObject each time it is called.

In short:

  • Besides the fact that it does not compile ( getFooBar() should return const BigObject* ), there is still no reason to assume that this code is incorrect. There may be other problems that do it wrong.
  • Perhaps a better style should return const & rather than const * . Which you return has nothing to do with whether foobar should be a BigObject data BigObject .
  • Of course, there is no β€œjust” way to make a foobar pointer or smart pointer - or you will need additional code to create an instance of BigObject for the BigObject .
0
source

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


All Articles