Article From:

We discussed the composition of class members, especially member functions. When we define a class, we often define: constructors, destructors, other functions, and friend functions (friend functions are not necessary).

At the same time, we know that when we define an object, the constructor will be called; when the object is destroyed, the destructor will be called. Friend functions provide us another way to access members.

But there will be more details. Through some specific details, we can feel the behavior in some classes:

Class declarations are as follows:

 1 # include "iostream"
 2 # ifndef STRNGBAD_H_
 3 # define STRNGBAD_H_
 4 class StringBad
 5 {
 6 private:
 7     char *str;
 8     int len;
 9     static int num_strings; //= 0;//It can be seen that in addition to const, members of a class cannot assign initial values internally.
10 public:
11     StringBad(const char * s);
12     StringBad();
13     ~StringBad();
15     friend std::ostream & operator<<(std::ostream & os, const StringBad & st);
16 };
17 # endif

In the above-mentioned declaration of this kind:

As usual: it includes constructors, destructors, and friend functions; but what needs to be emphasized here is the static member variables: num_strings. When we use StringBad class to generate A, B, C… class objects, A. str, B. str,C. str…. These variables are essentially addressed differently. But A. num_strings, B. num_strings, A. num_strings, addresses are the same. Namely:Static members belong to classes, not objects.

Let’s look at the definition of this class:

 1 # include "cstring"
 2 # include "strngbad.h"
 4 using  std::cout;
 6 int StringBad::num_strings = 0;
 8 StringBad::StringBad(const char*s)
 9 {
10     len = std::strlen(s);
11     str = new char[len + 1];
12     std::strcpy(str, s);
13     num_strings++;
14     cout << num_strings << ": \"" << str << "\" object created\n";
15 }
17 StringBad::StringBad()
18 {
19     len = 4;
20     str = new char[4];
21     std::strcpy(str, "C++");
22     num_strings++;
23     cout << num_strings << ": \"" << str << "\" default created\n";
24 }
26 StringBad::~StringBad()
27 {
28     cout << "\"" << str << "\" object created. ";
29     --num_strings;
30     cout << num_strings << "left\n";
31     delete[] str;
32 }
34 std::ostream & operator<<(std::ostream & os, const StringBad & st)
35 {
36     os << st.str;//Note that STR is printed here, if not str
37     return os;
38 }

For this class definition: let’s note that line 6 initializes static variables. Be careful,When num_strings are initialized, StringBad:: num_strings indicates that num_strings members belong to StringBad.

Question: Can I initialize in class declarations?

Answer: No. Class declarations only indicate what type of space needs to be allocated, but they do not really allocate space, so they cannot be initialized in declarations (except const).

From this class definition file, we can see that whether describing the class member function or the class member variable, we should describe its domain before!

new And delete are very important in dynamic memory allocation. Note, however, that new [] is used in the constructor and delete [] must exist in the destructor.

Continue with the call file:

 1 # include "iostream"
 2 using std::cout;
 3 # include "strngbad.h"
 5 void callme1(StringBad &);
 6 void callme2(StringBad);
 8 int main()
 9 {
10     using std::endl;
11     {
12         cout << "Starting an inner block.\n";
13         StringBad headline1("hello world!");
14         StringBad headline2("learning forever!");
15         StringBad sports("i love trvaling!");
16         cout << "headline1" << headline1 << endl;
17         cout << "headline12"<< headline2 << endl;
18         cout << "sports" << sports << endl;
19         callme1(headline1);
20         cout << "headline1" << headline1 << endl;
21        callme2(headline2);
22         cout << "headline2" << headline2 << endl;
23         cout << "Initialize one object to another:\n";
24         StringBad sailor = sports;
25         cout << "sailor: " << sailor << endl;
26     }
27     cout << "End of main()\n";
28     system("pause");
29     return 0;
30 }
32 void callme1(StringBad & rsb)
33 {
34     cout << "String passed by reference:\n";
35     cout << "     \n" << rsb << "\"\n";
36 }
38 void callme2(StringBad  sb)
39 {
40     cout << "String passed by rvalue:\n";
41     cout << "     \n" << sb << "\"\n";
42 }

Here, some details of line 19 and line 21 are given:

Line 19, the behavior of passing parameters is reference passing, 21 behavior: value passing.

Re-emphasize the two essential characteristics of value transfer and value return function:1. When the value is passed, the copy of the argument is copied so that the parameter is the copy of the argument; 2. When the value is returned, the copy of the return value (and destroy the variable (because the lifetime of the variable in the stack is the function call period) is copied, and the copy is subsequently operated.

So line 21: When passing by value, first create a copy of the object. But when creating a copy of an object, the constructor is called first, but who is the constructor? Is it the constructor in the previous class definition? If so, obviously the form of creating a copy should be:() or (cha*), but the question is: is this right?? Obviously not, what is that??? Actually, B = A assigns known object A to temporary object B, just like line 24. So which constructor does this initialization call??? In fact, it isOne is calledAssignment constructor, specially for this initialization of assignment operation.Obviously, there are no assignment constructors in previous declarations and definitions. Then the assignment constructor can only be generated by the compiler.

Finally, we know why we are required to adopt () or {} initialization instead of’=’initialization. Because usually we may not have defined our own assignment constructor as shown, there may be some risk that such a function will be generated by the compiler at this time. But if IInevitably, we will use the form of = or value transfer creation function. We should show the definition of our own assignment constructor to make our program less risky. (In fact, it’s usually safer to define a display assignment constructor)

Conclusion:Assignment constructors are called when objects are passed by value and returned by value. Creating a display assignment constructor in a 2 constructor is usually safer

Here we repeat: “Hello World!” Represents an address rather than a string.When we define: P = Hello Word! If we define q = p, do we copy the string P once and store it at the address of q? Not, but: p, Q all point to “hello world!” And at this time,”Hello World! “Actually, it looks more like an address.


Leave a Reply

Your email address will not be published. Required fields are marked *