C ++ chain element functions using .chain (). Method () vers & # 8594; clained (0-> method ()

Short version of the question

I ask for advice on whether to use. / * this versus β†’ / this, i.e. C ++ (* this) .chained (). methods () compared to this-> clained () -> methods ().

By the way, at the moment, most of the pages I've seen recommend [[C ++ (* this) .chained (). Methods ()]].

I'm just curious because you can't do

My_Class object.chained (). methods ();

(By the way, I did not test the examples in this first section. I provide proven examples in the second section.)

You have to do

My_Class object,
object.chained () methods () ;.

which is an annoying extra line

Or you can do

My_Class object = My_Class().object.chained().methods(); 

which requires copying values ​​is unacceptable if the constructor has side effects, for example, registering an instance of an object - like many Knobs libraries.

Or you can do

  My_Class* object_ptr = *(new My_Class).object.chained().methods(); 

which works, but requires annoying * (ptr)

Or you can do

  My_Class* object_ptr = (new My_Class)->object.chained()->methods(); 

which is best for teens.

I suppose you can do

my_class & object_ref (.. my_class () chain () methods ());

and I'm not sure what I think about it.

By the way, I don't need help for debugging here.
I program things like this all the time. I provide examples just for clarity.

I am looking for style tips because there are several ways to encode it, and I used different libraries that do this in reverse order.

And mixing them up is ugly:

  My_Object_with_Setters* object_ptr2 = &((new My_Object_with_Setters)->set_R1(1).set_P1(2)->set_R1(3)) My_Object().method_returning_ptr()->method_returning_ref(); 

Maybe this is not so bad .... but it certainly can be confusing.

When I run code that uses two different libraries using the mixed methods .chained () -> () I sometimes want you to have postfix addresses and dereference operators

My_Object * mptr = My_Object (). Method_returning_ptr () β†’ method_returning_ref β†’ &

More complete examples

Setter functions

I most often use this idiom with setter functions

 class My_Object_with_Setters { public: static int count; int value; public: My_Object_with_Setters() { ++count; value = 0; } public: std::ostream& print_to_stream(std::ostream& ostr) const { ostr << "(" << this->count << "," << this->value << ")"; return ostr; } friend std::ostream& operator<< ( std::ostream& ostr, const My_Object_with_Setters& obj ) { return obj.print_to_stream(ostr); } public: My_Object_with_Setters& set_R1(int val) { this->value = val; std::cout << "set_R1: " << *this << "\n"; return *this; } My_Object_with_Setters& set_R2(int val) { this->value = val; std::cout << "set_R2: " << *this << "\n"; return *this; } public: My_Object_with_Setters* set_P1(int val) { this->value = val; std::cout << "set_P1: " << *this << "\n"; return this; } My_Object_with_Setters* set_P2(int val) { this->value = val; std::cout << "set_P2: " << *this << "\n"; return this; } public: My_Object_with_Setters set_V1(int val) { this->value = val; std::cout << "set_V1: " << *this << "\n"; My_Object_with_Setters retval; retval = *this; // kluge to force new object return retval; } My_Object_with_Setters set_V2(int val) { this->value = val; std::cout << "set_V2: " << *this << "\n"; My_Object_with_Setters retval; retval = *this; // kluge to force new object return retval; } }; int My_Object_with_Setters::count = 0; // clas static, distinguishes instances void test_My_Object_with_Setters() { std::cout << "cascading ref, ref, copy, copy, ref, ref\n"; My_Object_with_Setters object; object.set_R1(1).set_R2(2).set_V1(11).set_V2(12).set_R1(101).set_R2(102); std::cout << "cascading ptr, ptr, ptr, ptr\n"; My_Object_with_Setters* object_ptr = (new My_Object_with_Setters)->set_P1(1)->set_P2(2)->set_P1(11)->set_P2(12); std::cout << "cascading &address-of, ptr, ptr\n"; (&object)->set_P1(1)->set_P2(2); std::cout << "cascading new ptr ref ptr ref\n"; My_Object_with_Setters* object_ptr2 = &(*(new My_Object_with_Setters)->set_R1(1).set_P1(2)).set_R1(3); } 

Test output:

 cascading ref, ref, copy, copy, ref, ref set_R1: (1,1) set_R2: (1,2) set_V1: (1,11) set_V2: (2,12) set_R1: (3,101) set_R2: (3,102) cascading ptr, ptr, ptr, ptr set_P1: (4,1) set_P2: (4,2) set_P1: (4,11) set_P2: (4,12) cascading &address-of, ptr, ptr set_P1: (4,1) set_P2: (4,2) cascading new ptr ref ptr ref set_R1: (5,1) set_P1: (5,2) set_R1: (5,3) 

General example

 class My_Object { public: static int count; public: My_Object() { ++count; } public: My_Object& method1_returning_ref_to_current_object() { std::cout << count << ": method1_returning_ref_to_current_object\n"; return *this; } My_Object& method2_returning_ref_to_current_object() { std::cout << count << ": method2_returning_ref_to_current_object\n"; return *this; } public: My_Object* method1_returning_ptr_to_current_object() { std::cout << count << ": method1_returning_ptr_to_current_object\n"; return this; } My_Object* method2_returning_ptr_to_current_object() { std::cout << count << ": method2_returning_ptr_to_current_object\n"; return this; } public: My_Object method1_returning_value_copy_of_current_object() { std::cout << count << ": method1_returning_value_copy_of_current_object\n"; My_Object retval; return retval; } My_Object method2_returning_value_copy_of_current_object() { std::cout << count << ": method2_returning_value_copy_of_current_object\n"; My_Object retval; return *this; } }; int My_Object::count = 0; // clas static, distinguishes instances void test_My_Object() { std::cout << "cascading ref, ref, copy, copy, ref, ref\n"; My_Object object; object .method1_returning_ref_to_current_object() .method2_returning_ref_to_current_object() .method1_returning_value_copy_of_current_object() .method2_returning_value_copy_of_current_object() .method1_returning_ref_to_current_object() .method2_returning_ref_to_current_object() ; std::cout << "cascading ptr, ptr, ptr, ptr\n"; My_Object* object_ptr = new My_Object; object_ptr ->method1_returning_ptr_to_current_object() ->method2_returning_ptr_to_current_object() ->method1_returning_ptr_to_current_object() ->method2_returning_ptr_to_current_object() ; std::cout << "cascading &address-of, ptr, ptr\n"; (&object) ->method1_returning_ptr_to_current_object() ->method2_returning_ptr_to_current_object() ; std::cout << "cascading new ptr ref ptr ref\n"; My_Object* object_ptr2 = (&(*(new My_Object) ->method1_returning_ptr_to_current_object()) .method2_returning_ref_to_current_object()) ; } 

Test output

 cascading ref, ref, copy, copy, ref, ref 1: method1_returning_ref_to_current_object 1: method2_returning_ref_to_current_object 1: method1_returning_value_copy_of_current_object 2: method2_returning_value_copy_of_current_object 3: method1_returning_ref_to_current_object 3: method2_returning_ref_to_current_object cascading ptr, ptr, ptr, ptr 4: method1_returning_ptr_to_current_object 4: method2_returning_ptr_to_current_object 4: method1_returning_ptr_to_current_object 4: method2_returning_ptr_to_current_object cascading &address-of, ptr, ptr 4: method1_returning_ptr_to_current_object 4: method2_returning_ptr_to_current_object cascading new ptr ref ptr ref 5: method1_returning_ptr_to_current_object 5: method2_returning_ref_to_current_object 

By the way, I don't need help for debugging here. I provide examples only for clarity.

I am looking for style tips.

+4
source share
2 answers

Each has its own style; as you say, it is only very annoying when you start mixing them.

Personally, I only return a pointer from a function if it can be 0; this never 0, so I always return *this (i.e. the link) and thus get in touch with . .

For what it's worth, I also try very hard to make a cheap default constructor, partly because there are so many cases where it turns out that it is convenient for the first default construct and then assigns.

+6
source

The best answer I can give is to be consistent. If the rest of your code uses this-> , use this. If he uses (*this). use it.

Since the difference is only in syntactic sugar, your best guide is what the other code you use does. Most people I know will prefer the -> syntax, but if you integrate into existing libraries, you can skip it.

Personally, I would use an additional initialization method. It reads the cleanest for me, one line builds the object on the stack, others call methods as needed. If you only need methods, and they do not depend on the actual object, I would make them static and skip creating the object together.

+2
source

All Articles