[C++] Classes and objects (below) -- initialization list -- static members -- friends -- internal classes, this article will give you an in-depth understanding.

Table of contents

1. Talk about the constructor again

1.1 Constructor body assignment

1.2 Initialization list

1.2.1 The meaning of the initialization list

1.3 explicit keyword

2. Static members

2.1 Problem introduction

2.2 Features

3, Tomomoto

3.1 Friend functions

3.2 Friend class

4. Internal class


1. Talk about the constructor again

1.1 Constructor body assignment

When creating an object, the compiler calls the constructor to give each member variable in the object an appropriate initial value.

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

We try to give all defaults to the constructor, so that even if we want to instantiate an object without parameters, this function will take care of it.

Although the object already has an initial value after the above constructor is called, it cannot be called the initialization of the member variables in the object. The statement in the constructor body can only be called the initial value assignment , not the initial value. initialization. Because the initialization can only be initialized once, and the constructor body can be assigned multiple times.

1.2 Initialization list

Initialization list: Starts with a colon , followed by a comma-separated list of data members , each " member variable " followed by an initial value or expression enclosed in parentheses.

Let's continue to expand with the Date class:

class Date
{
public:

	// 初始化列表
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

private:
	// 成员变量声明
	int _year;
	int _month;
	int _day;
};

The above code is the implementation of the initialization list.

1.2.1 The meaning of the initialization list

The initializer list is where the member variables of an object are defined.

Some variables can only be initialized where they are defined:

a. const modified variable; b. reference variable; c. custom type (no default constructor).

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

private:
	int _a;
};
class Date
{
public:
	// 初始化列表
	Date(int year, int month, int day, int& i)
		: _year(year)
		, _month(month)
		, _day(day)
		, _x(1)
		, _refi(i)
		, _a(1)
	{}

private:
	// 成员变量的声明
	int _year;
	int _month;
	int _day;

	// 定义时必须初始化
	const int _x;
	int& _refi;
	A _a;
};

The default value of the member variable is given to the initialization list; if the initialization list does not explicitly define the built-in type, the compiler will define the default definition and give a random value; give the default and initialize the value to use the initialized value.

For the member variables that must be initialized when they are defined, let's look at the situation of assigning initial values ​​​​in the function body:

This is where things go wrong, so initializer lists are great for this.

Notice:

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 placed in the initialization list for initialization:
a. Reference member variables
b. const member variables
c. Custom type members (and the class has no default constructor)
3. Try to use initialization list initialization, because whether you use initialization list or not, for custom type member variables, you must first use initialization list initialization.
4. The order in which member variables are declared in the class is the order in which they are initialized in the initialization list , regardless of their order in the initialization list

After initialization, it can also be assigned a value in the constructor body.

1.3 explicit keyword

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.

class Date
{
public:
	Date(int year)
		: _year(year)
	{}

private:
	int _year;
};
int main()
{
	Date d1(2023);

	// 隐式类型转换
	Date d2 = 2023;// 2023调用构造函数生成临时对象,再用临时对象去拷贝构造d2	

	return 0;
}

2023 is of int type, but no error is reported when assigning it to d2 of Date type, because 2023 has an implicit type conversion. If we don't want hermit type conversion to occur, add an explicit modifier before the constructor.

Decorating the constructor with explicit will prohibit the implicit conversion of the constructor.

Extension:

In C++11, implicit type conversion of multiple parameters is allowed.

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	// 隐式类型转换
	Date d = { 2023, 8, 7 };// 多参数隐式类型转换要用{}将多个参数括起来	
	const Date& d1 = { 2023, 1, 1 };// 转换时生成的临时变量具有常性,因此需要const修饰
    
	return 0;
}

Multi-parameter implicit type conversion needs to use {} to enclose multiple parameters.

2. Static members

2.1 Problem introduction

Let's count how many of Class A have been created and how many are being used?

// 创建了多少个对象
int n = 0;
// 正在使用的有多少个对象
int m = 0;
class A
{
public:
	A() 
	{ 
		++n;
		++m;
	}
	A(const A & t) 
	{
		++n;
		++m;
	}
	~A() 
	{
		--m;
	}
};
A func(A aa)
{
	return aa;
}
int main()
{
	A a1;
	A a2;
	cout << n << " " << m << endl;

	A();// 匿名对象,在本行创建并在本行销毁
	cout << n << " " << m << endl;

	func(a1);
	cout << n << " " << m << endl;

	return 0;
}

operation result:

We can see that global variables are achievable, but global variables are not safe (global variables can be modified globally), and are not conducive to encapsulation , so we don't use this method.

Let's change the method to achieve this:

We put the counter in the class and define it as private, but this is still not enough. Every time we create an object, there will be an n and m. Our purpose is that the object is public, so we use static modification, which solves the problem These two questions.

class A
{
public:
	A() 
	{ 
		++n;
		++m;
	}
	A(const A & t) 
	{
		++n;
		++m;
	}
	~A() 
	{
		--m;
	}

//private:
	// 创建了多少个对象
	static int n;
	// 正在使用的有多少个对象
	static int m;
};
int A::n = 0;
int A::m = 0;

A func(A aa)
{
	return aa;
}
int main()
{
	cout << "sizeof(A):" << sizeof(A) << endl;

	A a1;
	A a2;
	cout << A::n << " " << A::m << endl;

	A();// 匿名对象,在本行创建并在本行销毁
	cout << a1.n << " " << a2.m << endl;

	func(a1);
	cout << a1.n << " " << a2.m << endl;

	return 0;
}

After the static modification, n and m are static member variables, not of a certain object, but of all objects, so they do not exist in the object, but in the static area (sizeof is calculated as 1, which is an empty kind).

Secondly, the class is only the declaration of member variables. Static member variables must be defined outside the class, and there is no need to add static when defining.
If we define static members as private, we need to provide Get interface to access n, m

class A
{
public:
	A() 
	{ 
		++n;
		++m;
	}
	A(const A & t) 
	{
		++n;
		++m;
	}
	~A() 
	{
		--m;
	}

	int GetN()
	{
		return n;
	}
	int GetM()
	{
		return m;
	}

private:
	// 创建了多少个对象
	static int n;
	// 正在使用的有多少个对象
	static int m;
};
int A::n = 0;
int A::m = 0;

A func(A aa)
{
	return aa;
}
int main()
{
	A a1;
	A a2;
	cout << a1.GetN() << " " << a1.GetM() << endl;

	A();// 匿名对象,在本行创建并在本行销毁
	cout << a1.GetN() << " " << a1.GetM() << endl;

	func(a1);
	cout << a1.GetN() << " " << a1.GetM() << endl;


	return 0;
}

If we only create two anonymous objects and want to see n and m, no object cannot be called. If we create an object to call, this will affect n and m, so we also change the function to static, which You only need to add the class field to access it.

class A
{
public:
	A() 
	{ 
		++n;
		++m;
	}
	A(const A & t) 
	{
		++n;
		++m;
	}
	~A() 
	{
		--m;
	}

	// 静态成员函数的特点:没有this指针
	static int GetN()
	{
		return n;
	}
	static int GetM()
	{
		return m;
	}

private:
	// 静态成员变量属于所有A对象,属于整个类
	// 创建了多少个对象
	static int n;
	// 正在使用的有多少个对象
	static int m;
};
int A::n = 0;
int A::m = 0;

A func(A aa)
{
	return aa;
}
int main()
{
	A a1;
	A a2;
	cout << A::GetN() << " " << A::GetM() << endl;

	A();// 匿名对象,在本行创建并在本行销毁
	cout << A::GetN() << " " << A::GetM() << endl;

	func(a1);
	cout << A::GetN() << " " << A::GetM() << endl;

	return 0;
}

Note: Non-static member functions cannot be accessed in static member functions because there is no this pointer . Generally, statically modified member variables are used in conjunction with statically modified member functions.

2.2 Features

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 , and the static keyword is not added when defining, just 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 classes, subject to public, protected, and private access restrictions character limit

3, Tomomoto

Friend is divided into friend function and friend class.

Friends provide a way to break out of encapsulation, and sometimes convenience. But friends destroy encapsulation, so friends should not be used more often.

3.1 Friend functions

Question: In the previous article, we implemented all operators of the Date class that need to be overloaded. If we need to use cout to print custom type objects, we need to overload the stream caret.

The printed format is cout << " " << endl;

Then the overload stream caret cannot be inside the class. The first parameter in the class is the implicit this pointer, which will compete with cout for the first parameter position , so we can only define it outside the class.
If we put it outside, we can't access the class member variables, so it's our friend's turn to play here.

A friend function can directly access the private members of the class . It is an ordinary function defined outside the class and does not belong to any class, but it needs to be declared inside the class, and the friend keyword needs to be added when declaring.

class Date
{
	// 友元
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, 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;
};

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

int main()
{
	Date d1;
	cin >> d1;
	cout << d1;
}

operation result:

illustrate:

Friend functions can access private and protected members of the class, but not member functions of the class
. Friend functions cannot be modified with const (friend functions do not have this pointer).
Friend functions can be declared anywhere in the class definition and are not limited by class access Specifier restrictions
A function can be a friend function of multiple classes
The principle of calling a friend function is the same as that of an ordinary function

3.2 Friend class

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

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
{
	// 友元
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

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;

};
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

1. 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.
2. The friendship 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.

4. Internal class

Concept: If a class is defined inside another class, the inner class is called an inner class. It is to define another class in a class . The inner class is an independent class. It does not belong to the outer class, and it is not possible to access the members of the inner class through the object of the outer class. Outer classes do not have any privileged access to inner classes.

class A
{
public:
	class B
	{

	private:
		int _b;
	};
	int _a;
};
int main()
{
	cout << sizeof(A) << endl;
}

The printout here is 4, so we can know that sizeof (external class) = the size of the outer class. Although the inner class is nested inside, the inner class is not counted in the size of the outer class.

Second, the inner class is by default the friend class of the outer class.

class A
{
public:
	class B
	{
	public:
		void func()
		{
			A a;
			a._a = 1;
			cout << a._a << endl;
		}

	private:
		int _b;
	};

private:
	int _a;
};
int main()
{
	A::B b;
	b.func();

	return 0;
}

We define a function in class B, which accesses the member variable _a of class A and assigns it a value to see if it can run normally.

We can see here that the member variables of class A can be accessed in class B and assigned values. It can be seen that class B is a friend of class A.

However, class A is not a friend of class B, and friends are one-way.

characteristic:

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.

Guess you like

Origin blog.csdn.net/Ljy_cx_21_4_3/article/details/132200058