C + + Beginner Level -- classes and objects (Part 2)

[write before]

This article is a conclusion and supplement to classes and objects

1, On constructors

๐Ÿ’ฆ Constructor body assignment

โ“ Export initialization list โ”

class A
{
public:
	A(int a = 0)
	{
		_a = a;	
	}
private:
	int _a;
};
class B
{
private:
	int _b = 1;
	A _aa;
};
int main()
{
	B b;
	return 0;
}

๐Ÿ“ explain

For B, if we do not write the constructor, the compiler will generate by default - the built-in type will not be processed, and the user-defined type will call its default constructor processing (parameterless, full default and compiler generated by default). Note that there can only be one parameterless and full default. If the compiler is written, it will not be generated, and if it is not written, it will be generated by default. There is a bad handling in C + + - built-in types are not handled, and custom types are handled. To solve this problem, another patch is made in C++11 - a default value can be added after the built-in type. When you don't initialize it, it will initialize with the default value. This is a defect in the early design of C + +.

class A
{
public:
	A(int a = 0)
	{
		_a = a;	
		cout << "A(int a = 0)" << endl;
	}
	A& operator=(const A& aa)//It's OK not to write, because there are only built-in types, which can be completed by default
	{
		cout << "A& operator=(const A& aa)" << endl;
		if(this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
private:
	int _a;
};
class B
{
public:
	B(int a, int b)
	{
		//_ aa._a = a;//err: unable to access private member
		
		/*A aa(a);
		_aa = aa;*/ 
		_aa = A(a);//Simplified version, ibid
		
		_b = b;
	}
private:
	int _b = 1;
	A _aa;
};
int main()
{
	B b(10, 20);
	return 0;
}

๐Ÿ“ explain

Right up_ b can only be initialized to 1_ a can only be initialized to 0 โ“

Initialization can be shown here, using anonymous objects to initialize_ a.

However, this method is expensive (see the figure below).

๐Ÿ’ฆ Initialization list

Initialization list: start 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.

class A
{
public:
	A(int a = 0)
	{
		_a = a;	
		cout << "A(int a = 0)" << endl;
	}
	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if(this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
private:
	int _a;
};
class B
{
public:
	B(int a, int b)
		:_aa(a)
	{
		_b = b;
	}
private:
	int _b = 1;
	A _aa;
};
int main()
{
	B b(10, 20);
	return 0;
}

๐Ÿ“ explain

You can see that compared with function body initialization, initialization list initialization can improve efficiency - note that for built-in types, there is no difference between using function body or initialization list to initialize; But for custom types, using initialization lists is more valuable. It should also be noted here that the initialization and initialization list in the function body can be mixed.

โ“ Try to use the initialization list for initialization, because no matter whether you use the initialization list or not, for custom type member variables, you will use the initialization list first โ”

What members must be initialized with an initialization list โ“

class A
{
public:
	A(int a)
		:_a(a)
 	{}
private:
	int _a;
};
class B
{
public:
	B(int a, int ref)
 		:_aobj(a)
 		,_ref(ref)
 		,_n(10)
	{}
private:
	A _aobj;//There is no default constructor
	int& _ref;//quote
	const int _n;//const 
};

โš  be careful

one ๏ธโƒฃ Each member variable can only appear once in the initialization list (same as the definition) (initialization can only be initialized once).

two ๏ธโƒฃ Class contains the following members, which must be placed in the initialization list for initialization:

   1. Reference member variables (reference members must be initialized at the time of definition)

   2. Const member variable (members of const type must be initialized at the time of definition)

   3. Custom type members (this class has no default constructor)

โ“ 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 the order in which they appear in the initialization list โ”

#include<iostream>
using namespace std;
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(1);
	aa.Print();	
}

๐Ÿ“ explain

Program output above โ“

A. 1 โ€ƒ1

B. Program crash

C. Compilation failed

D. 1   random value

The output result of the above program is the D option, because C + + stipulates that the declaration order of member variables in the class is their initialization order in the initialization list, regardless of the order in which they appear in the initialization list. In practice, it is recommended that the declaration order be consistent with the initialization list order to avoid such problems.

๐Ÿ’ฆ explicit keyword

class A
{
public:
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;	
	}
	A(const A& aa)
	{
		cout << "A(const A& aa)" << endl;	
	}
private:
	int _a;
};
int main()
{
	A aa1(1);
	A aa2 = 1;
	return 0;
}

๐Ÿ“ explain

A aa2 = 1; Same as A aa1(1); This is the syntax supported by C++98. It is essentially an implicit type conversion - converting int to A. why can int be converted to a—— Because it supports a constructor that initializes a with an int parameter. Although the results of both of them are the same, they call the constructor directly, but the process is different for the compiler.

๐Ÿณ verification

๐Ÿ”‘ expand

For the knowledge of compiler optimization and underlying mechanism, you can learn about the deep exploration of C + + object model

โ“ Implicit type conversion is not allowed if you want to โ”

The keyword explicit can be used here

explicit A(int a)
	:_a(a)
{
	cout << "A(int a)" << endl;	
}

error C2440: cannot convert from int to A

โ“ Multi parameter implicit type conversion โ”

class A
{
public:
	A(int a1, int a2)
		:_a(a1)
	{
		cout << "A(int a1, int a2)" << endl;	
	}
	A(const A& aa)
	{
		cout << "A(const A& aa)" << endl;	
	}
private:
	int _a;
};
int main()
{
	A aa1(1, 2);
	//A aa2 = 1, 2;//???
	A aa2 = {1, 2};
	return 0;
}

๐Ÿ“ explain

A aa2 = 1, 2; ๏ผŸ๏ผŸ๏ผŸ

Obviously, C++98 does not support multi parameter implicit type conversion, but C++11 does -- aa2 = {1, 2}; Similarly, the compiler will still optimize.

When we use explicit keyword restriction, it will cause error C2440: unable to convert from initializer list to A

2, static member

๐Ÿ’ฆ concept

โ“ Write a program to calculate how many objects the program constructs (construction + copy construction) โ”

int countC = 0;
int countCC = 0;
class A
{
public:
	A()
	{
		++countC;
	}
	A(const A& a)
	{
		++countCC;
	}
};
A f(A a)
{
	A ret(a);
	return ret;
}
int main()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
	cout << countC << endl;
	cout << countCC << endl;
	return 0;
}

๐Ÿ“ explain

In this way, although the results can be calculated, there is a problem. countC and countCC can be changed at will, which is very bad.

Optimize โ“

class A
{
public:
	A()
	{
		++_count;
	}
	A(const A& a)
	{
		++_count;
	}
	int GetCount()
	{
		return _count;	
	}
	static int GetCount()
	{
		return _count;	
	}
private:
	int _a;
	static int _count;
};
//Define initialization
int A::_count = 0;
A f(A a)
{
	A ret(a);
	return ret;
}
int main()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
	cout << sizeof(A) << endl;
	
	//This shows that static members belong to the whole class and are shared by each defined object, but they are limited to public
	/*cout << A::_count << endl;	
	cout << a1._count << endl;
	cout << a2._count << endl;*/

	/*A ret;
	cout << ret.GetCount() - 1 << endl;*/
	/*cout << A().GetCount() - 1 << endl;*/
	cout << A::GetCount() << endl;
	
	return 0;
}

๐Ÿ“ explain

int _a; Exists in the defined objects and belongs to the object.

static int _count; There are static areas, which belong to the whole class and are shared by each defined object. Compared with global variables, it is limited by class fields and access qualifiers, which better reflects encapsulation and cannot be easily modified by others.

static member โ“

For non static members, their definitions are in the initialization list, but in C + +, static static member variables cannot be defined and initialized inside the class. The inside here is just a declaration. Note that although this is a private member, static members can be defined externally without adding static. sizeof does not calculate the size of static members during calculation.

_ count is private. How can I access it โ“

Define a public function GetCount function and return_ count:

Call,

1. Finally, after instantiating the object, call the GetCount function and reduce it by 1.

2. Direct anonymous object and subtract 1

3. Define the GetCount function as a static member function and call it using the class field

๐Ÿ’ฆ characteristic

one ๏ธโƒฃ Static member variables are shared by all class objects and do not belong to a specific instance.

two ๏ธโƒฃ Static member variables must be defined outside the class without adding the static keyword.

three ๏ธโƒฃ Class static members are available class names: static members or objects Static members.

four ๏ธโƒฃ Static member functions have no hidden this pointer and cannot access any non static members.

five ๏ธโƒฃ Like ordinary members of a class, static members also have three access levels: public, protected and private, and can also have return values.

[interview question 1]

The role of static C language | C++ โ“

C language:

1. Static modifies local variables and changes the life cycle of local variables (essentially changing the storage type of variables). Local variables change from stack area to static area, and the life cycle is the same as that of global variables

2. static modifies a global variable so that it can only be used inside its own file, while ordinary global variables can be used by the whole project

   โ“ Why can global variables be used inside other files โ”

     because global variables have external link attributes; However, after being modified by static, it becomes an internal link attribute, and other source files cannot be linked to this static global variable

3. Static modifies a function so that it can only be used inside its own file. In essence, static changes the external link attribute of a function into an internal link attribute (the same as static modifies a global variable)

C++:

1. Modify member variables and member functions. Member variables belong to the whole class, all objects are shared, and member functions have no this pointer.

[interview question 2]

Can static member functions call non static member functions โ“

No, because the static member function has no this pointer.

Can non static member functions call static member functions โ“

Yes, because non static member functions have this pointer.

3, A new method of member initialization in C++11

class A
{
public:
	A(int a = 0)
		: _a(0)
	{}
private:
	int _a;
};
class B
{
private:
	//Default value 
	int _b = 0;
	int* p = (int*)malloc(sizeof(int)*10);
	A _aa = A(10);//Construct first and then copy, optimize to construct
	A _aa = 10;//Ibid., recommendation
	//static int _n = 10;//err, static variables cannot be given default values
};
int main()
{
	B bb;	
	return 0;
}

๐Ÿ“ explain

C++11 supports initialization and assignment of non static member variables during declaration, but it should be noted that this is not initialization, but the default value of declared member variables - if the value is displayed in the constructor, the default value will not be used, and if it is not displayed in the constructor, the default value will be used. We mentioned it here in the last article. Here are some things we didn't mention in the last article.

4, Friends

Friends are divided into: friend function and friend class

Friends provide a way to break through encapsulation, sometimes providing convenience. However, friends will increase the degree of coupling and destroy the package, so friends should not be used more.

๐Ÿ’ฆ friend function

โ“ Heavy load<< โ”

class Date
{
	friend ostream& operator<<(ostream& out, const Date& d);//Friends, the position can be arbitrary, usually the beginning
	friend istream& operator>>(istream& in, Date& d);//Friends
public:
	Date(int year = 0, int month = 0, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	/*void operator<<(ostream& out)
	{
		out << _year << "/" << _month << "/" << _day << endl;
	}*/
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& out, const Date& d)//Support continuous output
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)//Support continuous input
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1, d2;
	cin >> d1 >> d2;

	//cout << d1 ? d1 << cout
	//cout << d1;
	/*d1.operator<<(cout);
	d1 << cout;*/
	cout << d1 << d2;
	return 0;
}

๐Ÿ“ explain

How to receive cin | cout โ“

In C + +, cout is an ostream object; cin is an object of istream.

cout << d1 | d1 << cout(d1.operator<<(cout)) โ“

Why not cout < < D1?

As mentioned earlier, operators have several operands, and overloaded functions have several parameters. If there are two operands, the left operand is the first argument and the right operand is the second argument.

In the Date class member function operator < <, the object is the first parameter. Because the implicit this pointer has been occupied by default, cout can only be used as the second operand. It can be inverted or inverted, but it does not conform to the original characteristics of the confluence operator.

How cout < < D1?

That is, take cout as the first parameter, so we can't use the member function here. Before, we used the member function because the member variable is private.

How to choose: use member functions | use global functions. Here, the readability of member functions is poor, which has a great impact, so they are abandoned and global functions are used.

How to solve the problem after supporting cout < < D1?

Solution 1: provide public member functions GetYear, GetMonth and GetDay

Solution 2: friend function

Here we introduce friends. By default, C + + cannot access private data outside the class, but it provides friends to help us solve the problem of this scenario. 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, but needs to be declared inside the class. When declaring, you need to add the friend keyword.

cout << d1 << d2; โ“

Note that the large phase diameter is the same as that of continuous assignment, but the direction is opposite.

๐Ÿ’จ Summary

1. Friend functions can access private and protected members of a class, but not member functions of a class.

2. Friend functions cannot be modified with const, because const modifies the object pointed to by this pointer.

3. Friend functions can be declared anywhere in the class definition and are not limited by the class access qualifier.

4. A function can be a friend function of multiple classes.

5. The calling principle of friend function is the same as that of ordinary function.

Note that some of the above concepts need to be combined with the following knowledge, and those who do not understand can be ignored first.

๐Ÿณ expand

Here we mainly expand the ability to solve code errors. Let's look at a piece of code first.

class A
{
	friend void f(const A& aa, const B& bb);
public:
	A(int a = 0)
		: _a(0)
	{}
private:
	int _a;
};
class B
{
	friend void f(const A& aa, const B& bb);
private:
	int _b = 0;
	A _aa;
};
void f(const A& aa, const B& bb)
{
	cout << aa._a << endl;	
	cout << bb._b << endl;
}
int main()
{
	A aa;
	B bb;
	f(aa, bb);
	return 0;
}

๐Ÿ“ analysis

I believe that the vast majority of people here can rely on their own experience to solve most of the bug s. However, the error in the above code is hard to understand.

Note that when faced with this situation, sometimes the errors reported by the compiler are inaccurate. Here are two suggestions to help improve the ability to find bug s.

1. When there are many errors, you must look at the top error, because the following errors may be indirectly caused by the above errors.

2. Exclusion method: there are 100 lines of code (the program crashed). If you comment out part of the code and the program is normal, there is no doubt that the error is caused by the code at the comment.

After analysis, we found that the error is: you can find a and B in class B friends; But you can't find B in friends of class A. So the solution is to add a pre declaration - class B;

๐Ÿ’ฆ Friend class

class Date; //forward declaration 
class Time
{
	friend class Date; //If you declare that the date class is a friend class of the Time class, you can directly access the private member variables in the Time class in the date class
public:
	Time(int hour, int minute, int second)
 	: _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)
{
	//Direct access to private member variables of time class
	_t._hour = hour;
 	_t._minute = minute;
	_t.second = second;
}
private:
	 int _year;
	 int _month;
	 int _day;
	 Time _t;
};

๐Ÿ“ analysis

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

1. Friend relationship is unidirectional and not exchangeable. For example, if the above Time class and Date class declare the Date class as its friend class in the Time class, you can directly access the private member variables of the Time class in the Date class, but you can't access the private member variables of the Date class in the Time class.

2. The friend relationship cannot be transferred. 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.

5, Inner class

Concept: if a class is defined inside another class, this inner class is called an inner class. Note that the internal class is an independent class at this time. It does not belong to the external class, and the internal class cannot be called through the object of the external class. External classes do not have any superior access rights to internal classes.

Note: the inner class is the friend class of the outer class. Note the definition of the friend class. The inner class can access all members of the outer class through the object parameters of the outer class. But the outside is not an internal friend.

characteristic:

1. Internal classes can be defined in public, protected and private of external classes.

2. Note that internal classes can directly access static, enumerating members and object / class names of external classes.

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

class A
{
private:
	static int k;
	int h;
public:
	class B//Class B is naturally a friend of class A
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;//ok
			cout << a.h << endl;//ok	
		}
	private:
		int _b;
	};
};
int A::k = 0;
int main()
{
	cout << sizeof(A) << endl;//4
	A::B b;//To define with B, you must specify a domain
	b.foo(A());
	return 0;
}

๐Ÿ“ explain

sizeof does not consider class B when calculating the size of type a objects. As the internal class of a, B is no different from ordinary classes. It is only defined inside A. It is limited by the class domain and access qualifier of A.

In general, internal classes are used only when one class serves another class. In fact, internal classes are not used much in C + + and are not as frequently as in JAVA. (for internal classes, use the following example to demonstrate)

๐Ÿ’ฆ Concept and characteristics

6, Exercises

1. Find 1 + 2 + 3 +... + n ๏ผœ difficulty coefficient โญ๏ผž

๐Ÿ“ A, C + switch + 2, a n d C + switch + 3.

Data range: 0 < n ≤ 200

Advanced: space complexity O(1), time complexity O(n)

๐Ÿ’จ Example 1:

Input: 5

Return value: 15

๐Ÿ’จ Example 2:

Input: 1

Return value: 1

๐Ÿงท Platform: Visual Studio 2017 & & windows

๐Ÿ”‘ Core idea: with the help of constructor and static keyword

Original question of nowcoder

class Sum
{
public:
    Sum()
    {
        _ret += _i;
        _i++; 
    }
    static int GetRet()//Access with guaranteed encapsulation_ ret
    {
        return _ret;
    }
private:
    static int _i;
    static int _ret;
};
//definition
int Sum::_i = 1;
int Sum::_ret = 0;

class Solution 
{
public:
    int Sum_Solution(int n) 
    {
        Sum a[n];//Call n constructions
        return Sum::GetRet();
    }
};

๐Ÿ“ explain

Sum a[n] can call the construct n times. However, it should be noted that variable length arrays are syntax supported by C99. OJ is supported here. Sum* p = new A[n] can be used for non-c99 later;

Transformed into internal class as follows:

#include<iostream>
using namespace std;
class Solution 
{
private:
	//Internal class, Solution 1 ++ N, which better reflects the encapsulation.
	class Sum
	{
	public:
    	Sum()
    	{
        	_ret += _i;
        	_i++; 
    	}
	};
public:
    int Sum_Solution(int n) 
    {
        Sum a[n];//Construct n calls
        return _ret;
    }
private:
	static int _i;
	static int _ret;
};
int Solution::_i = 1;
int Solution::_ret = 0;

2. Calculate conversion from date to day ๏ผœ difficulty coefficient โญ๏ผž

๐Ÿ“ Caption: according to the entered date, it is the day of the year. Ensure that the year is 4 digits and the date is legal.

Advanced: time complexity: O(n)\O(n), space complexity: O(1)\O(1)

Input Description: enter a line, each line is separated by spaces, which are year, month and day respectively

Output Description: the output is the day of the year

๐Ÿ’จ Example 1:

Input: December 31, 2012

Return value: 366

๐Ÿ’จ Example 2:

Input: 1982 3 4

Return value: 63

๐Ÿงท Platform: Visual Studio 2017 & & windows

๐Ÿ”‘ Core idea: We used to store the days of each month in a year. Here we store the days before the current month. The corresponding month + days is the current default days, and then judge the leap year.

Original question of nowcoder

#include<iostream>
using namespace std;
int main()
{
	//static int monthDays[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31,30,31};
    //reform:
    static int monthDays[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
    //2021 10 18
    int year, month, day;
    cin >> year >> month >> day;
    int n = monthDays[month-1] + day;
    //Judge February. Note that this is not equal to but greater than
    if(month > 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
    {
        n++;    
    }
    cout << n << endl;
    return 0;
}

๐Ÿ“ explain

Like JAVA language, it is not allowed to do this. We must do a class.

3. Date difference ๏ผœ difficulty coefficient โญ๏ผž

๐Ÿ“ Title: there are two dates. Find the number of days between the two dates. If the two dates are continuous, we specify the number of days between them as two days.

Input Description: there are multiple groups of data. Each group of data has two rows, representing two dates respectively, in the form of YYYYMMDD

Output Description: output one line for each group of data, i.e. date difference

๐Ÿ’จ Example 1:

Input:

20110412

20110422

Output: 11

๐Ÿงท Platform: Visual Studio 2017 & & windows

๐Ÿ”‘ Core idea:

Date - date โ“

The conventional idea is to make up the middle of the daily, monthly and annual reductions, but it is difficult to achieve in practice.

Idea 1: map a day of a month to 365 days

Idea 2 (optimal): first compare the size of the two dates, and then let the small one + +. When the small one is equal to the large one, the number of times they are added is the number of days they differ

Original question of nowcoder

//Ben pea wrote lazily

4. Print date ๏ผœ difficulty coefficient โญ๏ผž

Original question of nowcoder

5. Cumulative days ๏ผœ difficulty coefficient โญ๏ผž

Original question of nowcoder

7, Understand encapsulation again

C + + is an object-oriented program. Object-oriented has three characteristics: encapsulation, inheritance and polymorphism.

Through classes, C + + combines the attributes and behaviors of an object to make it more in line with people's cognition of a thing, and packs all the things belonging to the object together; Through the access qualifier, some of its functions are selectively opened to interact with other objects. For some implementation details inside the object, external users do not need to know, and it is useless in some cases. On the contrary, it increases the difficulty of use or maintenance and complicates the whole thing.

Let's take an example to better understand the benefits of encapsulation, such as traveling by train

Essentially, encapsulation is for better management:

Ticketing system, responsible for ticketing - users enter by ticket and take their seats according to the number

Staff, ticket sales, consultation, security inspection, preservation, health, etc

Train, take the user to the destination

With the cooperation of all staff in the railway station, we can take the train in an orderly manner without knowing the structure of the train and how the ticketing system operates, as long as it can be applied normally and conveniently.

Compared with the railway station next door where a San doesn't have packaging, the benefits of packaging are reflected incisively and vividly.

8, Understand object-oriented again

Object oriented is actually simulating the abstract mapping of the real world. Of course, this does not mean that object-oriented has been thoroughly learned. This is just an introduction.

Keywords: C++ Back-end

Added by The_Black_Knight on Tue, 15 Feb 2022 09:33:39 +0200