C++: Classes and Objects (Part 2)

1. Let’s talk about the constructor again:

Constructor body assignment:

Review: When creating an object, the compiler gives an appropriate initial value to each member variable in the object by calling the constructor.

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

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

Initialization list:

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

        Built-in types default to random values, and custom types will call their default constructors! ! !

class A 
{
public:
	A(int a)
		:_a(a)
	{
		cout << "A(int a = 0)" << endl;
	}
private:
	int _a;

};

namespace LFH
{
	class Date
	{
	public:
		//Date(int year, int month, int day)
		//{
		//	// 函数体内初始化
		//	_year = year;
		//	_month = month;
		//	_day = day;
		//}

		//Date(int year, int month, int day)
		//	:_year(year)
		//	,_month(month)
		//	,_day(day)
		//	,_aa(year)
		//	, _ref(year)
		//	, _n(day)
		//{

		//}

		Date(int year, int month, int day)
			:_aa(year)
			, _ref(year)
			, _n(day)
		{
			// 函数体内初始化
			_year = year;
			_month = month;
			_day = day;
		}

	private:
		//声明
		int _year = 1; // 缺省值
		int _month = 1;
		int _day;

		A _aa;//无初始化的自定义类型
		int& _ref;//引用:定义时必须初始化
		const int _n;//const:定义时必须初始化(后面无法修改)
	};
};

int main()
{
	LFH::Date d1(2023, 11, 4);

	return 0;
}

Problems solved by the initialization list:
         1. Initialization must be displayed when defining: reference, const, custom type without default construction
         2. Some custom members want to control the initialization by themselves.
         Try to use the initialization list for initialization
 . Question: Can the constructor only need an initialization list? , no need to initialize the function body?
 Answer: No, because some initialization or checking work cannot be completed by the initialization list.
         For example: malloc opens up space, and it needs to be judged in the function body (they can be mixed) to determine whether the opening is successful.

[Note]
        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:

               2.1 Reference member variables
                2.2 const member variables
                2.3 Custom type members (and the class does not have a default constructor)

class Stack
{
public:
	Stack(int n = 2)
		:_a((int*)malloc(sizeof(int) * n))
		,_top(0)
		,_capacity(n)
	{
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}

		memset(_a, 0, sizeof(int) * n);
	}

private:
	int* _a;
	int _top;
	int _capacity;
};


class MyQueue
{
public:
	MyQueue(int n1 = 10, int n2 = 20)
		:p1(n1)
		,p2(n2)
	{

	}

private:
	Stack p1;
	Stack p2;

	int _size;
};
int main()
{
	MyQueue s1;

	return 0;
}

         3. Try to use an initialization list for initialization , because no matter whether you use an initialization list or not, custom type member variables must be initialized using an initialization list first.

class A
{
public:
	A(int a)
		:_a1(a)
		,_a2(_a1)
	{

	}

	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}

private:
	int _a2;
	int _a1;
};

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

	return 0;
}

Why does _a2 get a random value?

4. 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.

Suggestion: Keep the declaration order consistent with the initialization list order to avoid understanding problems (like the code above!)

explicit keyword:

        The constructor can not only construct and initialize objects, but also has the function of type conversion for a single parameter or a constructor that has default values ​​except for the first parameter.

 Built-in types are implicitly converted to custom types:
    because: A has an int single-parameter constructor (supports a parameter of the same type as the parameter)/(multiple parameters with default values). Implicit conversion occurs
    . Do not want implicit type conversion to occur: construction Add before function: explicit

class A
{
public:
	//explicit A(int a)
	A(int a)
		:_a(a)
	{

	}
};
int main()
{

	//内置类型隐式转换成自定义类型
	//因为:A有int单参数构造函数(支持一个与参数类型相同的参数)/(多参数带缺省值)发生隐式转换
	//不想隐式类型转换发生:构造函数前加:explicit
	A aa3 = 3;
	const A& ra = 3;
    
    return 0;
}

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;
};

int main()
{
    Date d1(2023, 11, 2);
	Date d2 = (2023, 11, 4);//逗号表达式参数为:4,结果:d2:(4,1,1)
	Date d3 = 2023;//d3:(2023,1,1)

	Date d4 = { 2023,11,4 };//{ }可以多参数转换
	const Date& d5 = { 2023,11,4 };

	return 0;
}

2.staticc members:

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

Question: Implement a class and count how many class objects are created in the program.

class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; }
	~A() { --_scount; }
	static int GetACount() { return _scount; }
private:
	static int _scount;
};
int A::_scount = 0;
void TestA()
{
	cout << A::GetACount() << endl;
	A a1, a2;
	A a3(a1);
	cout << A::GetACount() << endl;
}

characteristic:

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

 2. Static member variables must be defined outside the class. The static keyword is not added when defining. It is just declared in the class.

 

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 restricted by public, protected, and private access qualifiers (cannot be called at will)

Here we do a similar question.

Due to the limitation of knowledge reserve, we need to understand the concept of variable-length arrays here, that is: arr[n]. Variable-length arrays are supported in C99 and later, and in the OJ question, g++ is used to compile everything.

Programming questions:

 Asking for 1+2+3+...+n_Niuke Question Ba_Niuke.com (nowcoder.com)

class Sum
{
public:
    Sum()
    {
        _ret += _i;
        ++_i;
    }
    static int Getsum()
    {
        return _ret;
    }
private:
    static int _i;
    static int _ret;
};

int Sum::_i = 1;
int Sum::_ret = 0;


class Solution {
public:
    int Sum_Solution(int n) {
        Sum arr[n];//调用n次构造函数

        return Sum::Getsum();
    }
};

3. Youyuan:

        Friends provide a way to break through encapsulation and sometimes provide convenience. However, friends will increase coupling and destroy encapsulation, so friends should not be used more than once. Because of high coupling: if one goes wrong, it will affect the others!
Friends are divided into: friend functions and friend classes

Friend function:

        Because cout's output stream object and the implicit this pointer are preempting the position of the first parameter . The this pointer defaults to the first parameter, which is the left operand. However, in actual use, cout needs to be the first formal parameter object to be used normally. So operator<< needs to be overloaded into a global function . But it will also cause members outside the class to be unable to access. At this time, friends are needed to solve the problem.

class Date
{
    //友元声明
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
//全局
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}
int main()
{
	Date d;
	cin >> d;
	cout << d << endl;
	return 0;
}

        A friend function can directly access the private members of a class. It is an ordinary function defined outside the class and does not belong to any class. However, it needs to be declared inside the class . The friend keyword needs to be added when declaring . It can be understood that I am this type of friend. You treat me as a friend and give me the authority to enter and exit your home at will!

illustrate:

       1. Friend functions can access private and protected members of the class, but are not member functions of the class .
        2. Friend functions cannot be modified with const
.         3. Friend functions can be declared anywhere in the class definition and are not restricted by class access qualifiers.
        4. A function can be a friend function of multiple classes.
        5. The calling principle of friend functions is the same as that of ordinary functions.

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.  Friend relationships are one-way and non-commutative. (You treat me as a friend, but I may not necessarily treat you as a friend!) For example, the above-mentioned Time class and Date class, declare the Date class as its friend class in the Time class, then you can directly access the private members of the Time class in the Date class variables, but if you want to access the private member variables in the Date class in the Time class, you cannot.         The friend relationship is not transitive. If C is a friend of B and B is a friend of A, it cannot be said that C is a friend of A.         Friend relationships cannot be inherited. I will give you a detailed introduction to the inheritance position.
       




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;
};

4.Inner classes:

concept:

        If a class is defined inside another class, the inner class is called an inner class. The inner class is an independent class. It does not belong to the outer class, and the members of the inner class cannot be accessed through the objects of the outer class. The outer class does not have any superior access to the inner class.
Note: The inner class is the friend class of the outer class. See the definition of friend class. The inner class can access all members of the outer class through the object parameters of the outer class. But the outer class is not a friend of the inner class.

characteristic:

1. Inner classes can be defined as public, protected, or private in external classes.
2. Note that the inner class can directly access the static members in the outer class without the object/class name of the outer class.

3. sizeof (external class) = external class, has nothing to do with internal classes.

class A
{
private:
	int h;
public:
	class B
	{
	public:
		void func()
		{
			//内部类天生是外部类的友元
			A aa;
			++aa.h;
		}
	private:
		int _b;
	};
};

int main()
{
	//不计友元的大小
	cout << sizeof(A) << endl;

	A aa;
	A::B bb;//如果不是公有就无法这样调用
	//如果是private则只能通过A的成员函数调用B

	return 0;
}

B can access the member variables or member functions of A, but A cannot access the member variables or member functions of B. If class B is defined as private, then B cannot be used through A::B, but only through the members of A. function to use!

5. Anonymous objects: 

A() //The constructor will be called

class A
{
public:
	A(int a = 0)
	:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};
class Solution 
{
public:
	int Sum_Solution(int n) 
	{
		//...
		return n;
	}
};
int main()
{
	A aa1;
	// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
	//A aa1();
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
	A();
	A aa2(2);
	// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说
	Solution().Sum_Solution(10);
	return 0;
}

6. Extended knowledge (compiler optimization):

In the same expression, continuous construction + construction / construction + copy construction / copy construction + copy construction are combined into one

(Two calling methods are optimized into one)
         a. Construction + construction -> construction (optimization)
         b. Construction + copy construction -> construction (optimization)
         c. Copy construction + copy construction -> copy construction (optimization)

        Compilers in the past 10 years or so will perform the above optimizations. It should be noted that the above is performed in Debug mode. In release mode, there will be more in-depth optimizations. Since different compilers have different optimization levels, I will not go into details.

        If you compile with g++ on Linux, the default is to compile in release mode, and you can turn off optimization through -fno-elide-constructors!

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}

	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;

		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}

	~A()
	{
		cout << "~A()" << endl;
	}

private:
	int _a;
};

 a. Construction + construction -> construction (optimization)

int main()
{
	//构造+构造->构造(优化)
	A aa1 = 1;//先构造一个临时对象,再拷贝构造给aa1
	
	//可以通过下面观察知道应该先构造一个临时对象
	const A& raa = 3;

	return 0;
}

 b. Construction + copy construction -> construction (optimization)

void func(A aa1)
{
	
}

int main()
{
	//构造函数
	A aa(1);
	//拷贝构造
	func(aa);
	cout << "1111111" << endl;

	//当把它们写在一条表达式上时
	func(A(2));//构造+拷贝构造->构造(优化)
	cout << "2222222" << endl;

	//3构造(隐式转换成A类型的),再拷贝构造
	func(3);//构造+拷贝构造->构造(优化)

	return 0;
}

  c. Copy construction + copy construction -> copy construction (optimization)

A func()
{
	A aa;

	return aa;
}

int main()
{
	A aa1 = func();//拷贝构造+拷贝构造->拷贝构造(优化)
	cout << "1111111111" << endl;


	return 0;
}

7. Understand classes and objects again

        Physical computers in real life do not recognize it. Computers only recognize data in binary format. If you want the computer to recognize entities in real life, the user must describe the entity through some object-oriented language, and then write a program to create the object before the computer can recognize it. For example, if you want the computer to understand the washing machine, you need to:
        1. The user must first abstract the entity of the washing machine in reality - that is, understand the washing machine at the human level of thought, what attributes the washing machine has, and what functions it has, that is, abstract the washing machine. A process of cognition;
        2. After 1, the person has a clear understanding of the washing machine in his mind, but the computer is not clear at this time. If you want the computer to recognize the washing machine in your imagination, you need a person. Use some object-oriented language (such as: C++, Java, Python, etc.) to describe the washing machine as a class and input it into the computer;
        3. After 2, there is a washing machine class in the computer, but the washing machine class It only describes the washing machine object from the perspective of the computer. Through the washing machine class, specific washing machine objects can be instantiated. At this time, the computer can know what the washing machine is.
        4. Users can use the washing machine object in the computer to simulate the actual washing machine entity.
In the class and object stage, everyone must realize that a class describes a certain type of entity (object), describing what attributes and methods the object has. After the description is completed, a new custom type is formed. , specific objects can be instantiated using this custom type.

The above is my personal learning insights and analysis of the learning process. You are welcome to discuss and communicate in the comment area!
If this article is helpful to you, please support it.
Thank you guys for your support! Thank you guys for your support! Thank you guys for your support!

                                              

Guess you like

Origin blog.csdn.net/weixin_71964780/article/details/134216098