When should links be used in C ++?

I have been programming C ++ for a while, and I am beginning to doubt that a rule that uses references, whenever possible, should apply everywhere.

Unlike this related SO post . I'm interested in another matter.

In my experience, a link / pointer mixes your code:

std::vector<Foo *> &x = get_from_somewhere(); // OK? reference as return value some_func_pass_by_ref(x); // OK reference argument and reference variable some_func_by_pointer(x[4]); // OK pointer arg, pointer value some_func_pass_elem_by_ref(*x[5]); // BAD: pointer value, reference argument some_func_that_requires_vec_ptr(&x); // BAD: reference value, pointer argument 

One option is to replace & with * const as Foo & with Foo * const

 void some_func_by_ref(const std::vector<Foo * const> * const); // BAD: verbose! 

thus, at least the detours disappeared. and the function headers are being rewritten because all the arguments will be pointers ... at the price of contaminating the const code instead of pointer arithmetic (basically & and * ).

I would like to know how and when you apply the rule to use links whenever possible .

with considering:

  • minimal rewriting of function prototypes (for example: oh my gosh, I need to rewrite a lot of prototypes because I want to put this reference element in a container)
  • readability

    • avoid using * to convert Foo* to Foo& and vice versa
    • avoid excessive use of constants, as in * const

NOTES: one thing I decided is to use pointers when I intend to ever put an item in an STL container (see enhancement :: ref)

I don't think this is something specific to C ++ 03, but C ++ 11 solutions are great if they can be sent back to C ++ 03 (i.e.: NRVO instead of move semantics).

+4
source share
3 answers

The following methods seem reasonable:

  • boost and C ++ 11 have a class that can be used inexpensively to store links in a container: Reference Wrapper
  • A good tip is to use the idiom descriptor / body more often than going around source pointers. It also solves the problem of memory ownership, which is defined by a reference or pointer. Sean Parent from Adobe pointed this out to his native 2013.

I decided to use the Idle approach for Handle / Body because it gives pointers automatic copy / assign behavior, hiding the underlying implementation and property semantics. It also acts as a kind of compile-time firewall, reducing the inclusion of a header file.

0
source

When should links be used in C ++?

When you need to process the variable as the object itself (in most cases, when pointers are clearly not needed and you do not want to own the object).

I would like to know how and when you apply the rule to use links whenever possible .

if possible , unless you need to:

  • work with the address (register the address, diagnose or record your own memory allocation, etc.)
  • take ownership of the parameter (pass by value)
  • Observe an interface that requires a pointer (C compatibility code and old code).

Bjarne Stroustrup stated in his book that he introduced language references because the operators had to be called without creating a copy of the object (which would mean β€œby pointer”), and he needed to respect syntax similar to calling by value (this meant would be "not by pointer") (and, therefore, links were born).

In short, you should use pointers as little as possible:

  • if the value is optional ("may be empty"), then use std :: optional around it, not a pointer
  • if you need to take responsibility for the value, get the parameter by value (not a pointer)
  • if you need to read the value without modifying it, get the const & parameter
  • if you need to dynamically select or return a new / dynamically allocated object, pass the value with one of the following: std::shared_ptr , std::unique_ptr , your_raii_pointer_class_here - not a pointer (raw)
  • If you need to pass a pointer to C code, you will still need to use the std :: xxx_ptr classes and get the pointer using .get() to get the raw pointer.

I realized that I need to use pointers whenever I intend to ever put an element in an STL container (or can I get rid of this?)

You can use the Boost Pointer Container Library .

+7
source

IMHO the rule is because the source pointers are dangerous, because liability for property and destruction becomes unclear. Consequently, multiple encapsulations around the concept ( smart_ptr , auto_ptr , unique_ptr , ...). First consider using such encapsulations instead of the raw pointer in your container.

Secondly, why do you need to specify pointers in the container? I mean, they must contain complete objects; they have a allocator as a template argument for the exact allocation of memory in the end. In most cases, you need pointers because you have an OO approach that makes great use of polymorphism. You should reconsider this approach. For example, you can replace:

 struct Animal {virtual std::string operator()() = 0;}; struct Dog : Animal {std::string operator()() {return "woof";}}; struct Cat : Animal {std::string operator()() {return "miaow";}}; // can not have a vector<Animal> 

Something like this using Boost.Variant :

 struct Dog {std::string operator()() {return "woof";}}; struct Cat {std::string operator()() {return "miaow";}}; typedef boost::variant<Dog, Cat> Animal; // can have a vector<Animal> 

That way, when you add a new animal, you don’t inherit anything, you just add it to this option.

You can also consider a bit more complex, but more general, using Boost.Fusion :

 struct Dog {std::string talk; Dog() : talk("wook"){}}; struct Cat {std::string talk; Cat() : talk("miaow"){}}; BOOST_FUSION_ADAPT_STRUCT(Dog, (std::string, talk)) BOOST_FUSION_ADAPT_STRUCT(Cat, (std::string, talk)) typedef boost::fusion::vector<std::string> Animal; int main() { vector<Animal> animals; animals.push_back(Dog()); animals.push_back(Cat()); using boost::fusion::at; using boost::mpl::int_; for(auto a : animals) { cout << at<int_<0>>(a) << endl; } } 

Thus, you do not even change the aggregate version and algorithms on animals, you just need to provide FUSION_ADAPT, corresponding to the premises of the algorithms used. Both versions (variant and merge) allow you to define groups of orthogonal objects, a useful thing that you cannot do with inheritance trees.

+2
source

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


All Articles