C++ Classes and Objects 3: More details about class internals

Table of contents

Initialization list:

explicit keyword

​edit

 static member

Tomomoto

inner class

 anonymous object

 Some compiler optimizations when copying objects


 

We have already touched the constructor, its function can help us assign values ​​​​to variables very conveniently, but it is not initialization here, because a constructor can perform multiple assignments to several variables, which is contrary to the concept of initialization.

In short, the initialization method we mentioned above is relatively... counterfeit, as follows:

class Date
{
public:
 
	Date(int year=2022, int month=12, int day=29)
	{
		_year = year ;
		_month= month ;
		_day = day   ;
	}
 
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day  << endl;
	}
 
private:
	int _year;
	int _month;
	int _day;
};

 Therefore, the real process of initializing through the constructor is to use the initialization list.

Initialization list:

 Initialization list : starts with a colon, followed by a comma-separated list of data members, each "member variable" is followed by an
initial value or expression in parentheses, the two ways of writing and the effects of the two ways of writing are recorded as follows Same, just write according to your preferences

class Date
{
public:
    //写法一
	Date()
        :_year(2022)
        ,_month(1)
        ,_day(2)
	{}

    //写法二
    Date()
        :_year(2022),_month(1),_day(2)
	{}

private:
	int _year;
	int _month;
	int _day;
};

Each member variable can only appear once in the initializer list

The initialization list can also be mixed with expressions inside the function body, such as the initialization of the stack

Mixing is also necessary, the initialization list can't do everything.

 This thing doesn't look great, does it? What's the special attack?

The class contains the following members, which must be placed in the initializer list for initialization:

1. const member variable

2. Custom types without default constructors

3. Reference member variables


The first usage scenario: const member variables

  • Since const has only one chance to initialize, it is not allowed to initialize a member variable of const type within the function body of the constructor. Because the definition initialization phase has been passed at this time, and the definition initialization phase is the initialization list.

  • For built-in types, if we don't initialize the values ​​in the list, the compiler will give a random value, and the solution we used to deal with this situation before was the default value. Contacting the current knowledge, the default value is actually given The stage is when the initialization list is made, because all member variables will go through the initialization list anyway

 The second usage scenario: referencing member variables

class B
{
public :

	B(int use,int a)
		:_i(0)
		,_use(use)
	{
	}

private:
	const int _i;
	int& _use;
	A _A;

};

 Third usage scenario: custom types without default constructors

In fact, you may be a little bit puzzled by the description here, why must the initialization list be used to initialize the three brothers? It mainly depends on their specificity, such as references, which must be initialized when they are created, and the same is true for const objects.

So why do custom types without default constructors also go through the initialization list?

Let's review the previous knowledge below. When a class treats member variables, it does not deal with built-in types, but calls its default constructor for custom types.

Well, there is no custom type without a default constructor, as follows:


class A
{
public:
	A(int a )
		:_a(a)
	{}

private:
	int _a;
};

We can see that we have written a constructor, but it is not a default constructor. It needs to pass parameters to be initialized normally. In this case, if class B wants to successfully initialize this custom type, it must find Its initialization method, but it cannot be found at all, and an error will be reported

 So, in this case, why does the initialization list solve this problem?

In fact, in the C++ class, even if we don't write the initialization list, it will go, and each member will definitely go the initialization list. We are equivalent to directly passing parameters to it at this stage, and successfully making it initialize successfully, so as to solve this problem.

Summarized as follows:

It should be noted that the order in the initialization list is in the order of declaration. For example, the date class is in the order of declaration of year, month and day, and the order of the initialization list is the same.


The order in which member variables are declared in a class is the order in which they are initialized in the initialization list, regardless of their order in the initialization list

class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{}
	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};


explicit keyword

When the constructor of a class is a single-parameter constructor, we can construct with only one parameter assignment.

That is, directly Date d2 = 2022;

The reason why an object can be created directly with the int type is the principle of implicit type conversion.

Constructors can not only construct and initialize objects, but also have the function of type conversion for a single parameter or a constructor with default values ​​except for the first parameter, which has no default value.

That is, in the process of single-parameter construction, 2022 constructs an unnamed object, and finally uses the unnamed object to assign a value to the d2 object

The principle is shown in the figure above, but when we want to use a reference to create an object, an error will be reported if we use the reference directly, because a temporary variable will be generated when any conversion occurs, and this temporary variable is constant. Add a const can be established.

Of course, the conditions for this conversion to occur are not so harsh, and implicit type conversion will occur only if parameters need to be passed.

 static member

Sometimes we have to use global variables to achieve certain operations, but global variables themselves are not safe because they can be changed at will. Therefore, in order to use global variables or variables with global properties more safely, we can use static To modify the members of a class.

Before that, what we need to pay attention to is what is the difference between local static and global static?

The difference between local static variables and global static variables lies in their different scopes. Local ones can only be used within its scope, while global ones can be used anywhere. They are all stored in the static area and have the same life cycle. But the static inside the class belongs to the scope of the class.

 Class members declared static are called static members of the class, member variables modified with static are called static member variables; member functions modified with static are called static member functions. Static member variables must be initialized outside the class

Because as a member variable of a class, it is natural to initialize a member variable every time a class is created, but it does not make any sense to initialize N static properties of this variable every time a class is created, so static member variables must be in Initialization outside the class. Similarly, the default value cannot initialize static member variables.

 Defines the type to be initialized.

Of course, it is no problem to directly access it as a member variable belonging to this class.

all in all:


Looking back at the old question, will this be reported as an error?

 No, the reason why this paragraph can still run is that it is stored outside the object like member functions, and static can still be found in the static area.

Due to the special nature of static variables, we generally don't put them in public, but in private . In order to get its value, we write a function to get it.

 But in this way, every time you want to use this function, you need to create an object first, which is quite painful to use. Is there any way to get its value directly? At this time, you can use static member functions to solve this problem.

 As a static member function, it does not have a this pointer . Combined with the previous knowledge, the reason why we cannot directly call member functions beyond the class is mainly because of the existence of the This pointer, and if it does not, it can be called directly without creating an object.

 But this also brings some side effects, this function will not be able to access non-static member variables.

 To sum it up :

Static types mainly play the role of breaking the wall of classes in classes and objects, and its characteristics are very similar to similar authentication, only the same kind that are all static can be recognized by each other.

1. Static members are shared by all class objects, do not belong to a specific object, and are stored in the static area

2. Static member variables must be defined outside the class, the static keyword is not added when defining, and only declared in the class

3. Class static members can be accessed with class name:: static member or object. static member

4. Static member functions have no hidden this pointer and cannot access any non-static members

5. Static members are also members of the class, subject to the restrictions of public, protected, and private access qualifiers

Tomomoto

Youyuan, we have already touched it before. Simply put, it is to open a back door to steal home access to private variables and break through private encapsulation.

However, what we have been in contact with before is the friend function instead of the friend class. The friend class is actually not bad, and it mainly has the following properties.

All member functions of a friend class can be friend functions of another class, and can access non-public members of another class.

The friendship relationship is one-way and not exchangeable. For example, the above-mentioned Time class and Date class declare Date class as its friend class in the Time class, then you can directly access the private member variables of the Time class in the Date class, but want to access the private member variables of the Date class in the Time class not.

Friend relationship cannot be transmitted. If C is a friend of B and B is a friend of A, it cannot be explained that C is a friend of A. The friendship relationship cannot be inherited, and I will give you a detailed introduction in the inheritance position.

inner class

The concept of an inner class is actually a class in a class. Creating another class in a class is an inner class. The inner class has the following characteristics.

1. The inner class can be defined as public, protected, or private in the outer class.

2. Note that the inner class can directly access the static members of the outer class without the object/class name of the outer class.

3. sizeof (external class) = external class, has nothing to do with the inner class.

The characteristics of the inner class here are still very strange, B can access the members of A at will, but A cannot access B's.

 

 anonymous object

 We normally create objects in two ways:

 Anonymous objects are such

 The characteristic of an anonymous object is that its life cycle is only this line , which will be useful in some cases, such as a very simple member function encapsulated in a class

 To create an object, but anonymous objects can be used directly

 There is another application scenario:

Optimized to:

 Some compiler optimizations when copying objects

 Optimization Scenario 1:

Let's go back to the line of code where a single parameter can be used to create an object, that is, the block of the explicit keyword. We can only use a variable to trigger an implicit type conversion and then create an object. The generation process is to create an object with 1 Empty temporary object, and then use this temporary object to construct aa1

When we create this object, under the old version of the compiler, the steps to execute are:

1. First use 1 to construct an object of the same type

2. Copy the structure to aa1.

In modern compilers, the compiler has become more "responsible" for it to optimize this into a direct construct


 Optimization Scenario 2:

 

In this scenario, we use class A to create an aa1 object, and pass aa1 into f1 as a parameter. In this process, copy construction occurs when entering the function body

This is a construction plus copy construction, but this is not a sequential step, in order to ensure that the correctness after optimization is not changed, the compiler will not optimize this process. The optimization can be triggered by switching to the following creation method.


 Optimization Scenario 3:

 

Here, the native execution steps of this function are construction + copy construction

If there is a return value received, the compiler will optimize it into the following steps.

 The above can be optimized without creating a new object . The following will not work.

 


Optimization Scenario 4:

 

 The code comment part is a non-optimized writing method. In this non-optimized writing method, we first construct an aa, and then copy and construct a temporary object. Then, the temporary object triggers the copy construction again to return the value.

 The optimized writing method is as follows:

 All in all : by making good use of the optimization mechanism of the compiler, we can return directly, or create variables or something, without any intermediate process, and it is best to use it directly.


At this point, the overview of classes and objects is complete!

Thanks for reading, I hope it can help you a little bit!

Guess you like

Origin blog.csdn.net/m0_53607711/article/details/128518779