Technology Sharing

Constructor initialization list, static members, friends, inner classes [Classes and Objects (Part 2)]

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

PS: The following codes are all tested in the VS2022 environment, which does not mean that all compilers can pass.
PS: The test code does not show the declaration of the header file stdio.h. Please add it yourself when using it.

  

insert image description here

                                           Blogger homepage: LiUEEEEE
                                                C++ Column
                                              C language column
                                            Data Structure Column
                                            Sorting Algorithm Column
                                         Likouniuke Classic Questions Column

1. Initialization list of constructor

1.1 Constructor Review


In my previous blog

          Construction, destruction, copy [classes and objects (middle)]
The constructor of the class is mentioned in the previous section. Taking the date class as an example, the initialization method is as follows:

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

As shown in the above code, without further learning of C++, users usually think that the code in the constructor is the initialization of class member variables, but the fact is just the opposite. In C++, the main statement of the constructor only assigns values ​​to member variables and cannot be called initialization in the true sense, because initialization can only be done once, while the constructor can assign values ​​to member variables multiple times, for example:

	Date(int year, int month, int day)
	{
		_year = year;
		_year += 1;
	}
  • 1
  • 2
  • 3
  • 4
  • 5

1.2 Initialization List


At this time, we need to introduce the initialization list in C++.
The initialization list starts with a ":" (colon) and is a list of data members separated by "," (commas). Each member variable is followed by a parenthesis, and the value in the parentheses is the initialized value, for example:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
  • 1
  • 2
  • 3
  • 4
  • 5

have to be aware of is:

  1. Each member variable can only appear once in the initialization list (initialization can only be initialized once)
  2. The class contains the following members, which must be initialized in the initialization list:
    • Referencing member variables
    • User-defined type member variables (and the user-defined type has no default constructor: no-parameter constructor, all-default constructor, system-generated constructor)
    • const modified member variables

The custom member variables mentioned above (without default constructor) mean that the constructor of the defined custom member variables is a parameterized constructor, which requires passing parameters to be initialized. When the user explicitly implements the constructor, if no initialization list is written, the compiler will automatically generate it for the user. However, if the initialization of the custom member variables requires the participation of parameters, the system will not pass the parameters by default, which will result in uninitialized variables. The above precautions are to avoid uninitialized variables and to avoid unnecessary trouble in subsequent use.

1.3 Tips for initializing lists


When writing the initialization list, the member variable initialization order must be consistent with the declaration order in the class, because the compiler initializes the variables in the order of declaration. If the user does not write in order, it will cause unnecessary trouble, such as:
class A
{
public:
	A(int n)
		:_a2(n)
		,_a1(_a2)
	{}
	void Print()
	{
		cout << "_a1 = " << _a1 << endl;
		cout << "_a2 = " << _a2 << endl;
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	A aa(100);
	aa.Print();

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

insert image description here
When initialization is performed in the manner shown in the figure above, the expected results will be achieved, so such operations should be avoided when using it.



2. The explicit keyword


For the constructor, there is also a function dedicated toSingle-parameter constructororConstructor with multiple parameters but no default value for the first parameterImplicit type conversions, for example:
class Date
{
public:
	Date(int year)
		: _year(year)
	{}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

When assigning by passing a value, for example:

	Date date(2024);
  • 1

At the compiler level, 2024 is not directly assigned to the created class object, but 2024 is converted into a temporary class object through implicit conversion, and then this type of object is assigned to the class object that needs to be initialized. For example, the class object that needs to be initialized is A, and the temporary class object is B:

	Date A(2024);
	等价于
	Date A = B//B的值为2024
  • 1
  • 2
  • 3

In order to avoid implicit conversion, C++ created the explicit keyword. Adding explicit before the constructor can prevent implicit conversion from happening. For example:
	explicit Date(int year)
		: _year(year)
	{}
  • 1
  • 2
  • 3




3. Static members

3.1 static member concept


Class members declared as 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, for example:
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
	static int _i;
};

int Date::_i = 1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.2 Static Member Characteristics


  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. Do not add the static keyword when defining them.
  3. Class static members can be accessed using class name::static member or object.static member
  4. Static member functions do not have a hidden this pointer and cannot access any non-static members
  5. Static members are also members of the class and are subject to the restrictions of public, protected, and private access qualifiers.




4. Friends


Friends provide a way to break through encapsulation and sometimes provide convenience. However, friends increase coupling and break encapsulation, so friends should not be used too much.

Friends are divided into: friend functions and friend classes

4.1 Friend Functions


A friend function is a function outside a class that needs to access private members in a class. You can declare this function as a friend function of the class in the class so that it can be accessed. For example:
class Date
{
	friend void Print(Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
void Print(Date& d)
{
	cout << d._year << " " << d._month << " " << d._day <<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • Friend functions can access private and protected members of a class, but are not member functions of the class
  • Friend functions cannot be modified with const
  • Friend functions can be declared anywhere in a class definition and are not restricted by the class access qualifier.
  • A function can be a friend function of multiple classes
  • The calling principle of friend function is the same as that of ordinary function

4.2 Friend Classes


When a class wants to access the private members of another class, it can declare this class as a friend in the class that needs to be accessed, and then access its private members. For example:
class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
					   //中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
			: _hour(hour)
			, _minute(minute)
			, _second(second)
	{}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
	{}	
	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • The friend relationship is one-way and not commutative. For example, in the above Time class and Date class, if the Date class is declared as a friend class in the Time class, then the private member variables of the Time class can be directly accessed in the Date class, but the private member variables of the Date class cannot be accessed in the Time class.
  • The friend relationship cannot be transitive. If C is a friend of B and B is a friend of A, it does not mean that C is a friend of A.




5. Inner Class


C++ supports creating another class inside a class. The latter is called an inner class. An inner class is an independent class that does not belong to the outer class, and the members of the inner class cannot be accessed through the outer class object. The outer class does not have any superior access rights to the inner class.
And the inner class is naturally a friend of the outer class, that is, the inner class can access the private members of the outer class.

characteristic:

  1. Inner classes can be defined in public, protected, or private form of outer classes.
  2. Note that inner classes can directly access static members in outer classes without the need for the object/class name of the outer class.
  3. sizeof(outer class) = outer class, and has nothing to do with inner class.




6. Conclusion


insert image description here

  Thank you very much for reading my original article.
  This article is mainly for personal learning and knowledge sharing. The learning road is long. If there are any mistakes, thank you for correcting me.
  If you need to quote, please indicate the address.