C + + classes and objects

Posted by Seamless on Thu, 03 Mar 2022 06:21:07 +0100

                                     

The previous blog has introduced the syntax of classes and objects, but it also has some piecemeal syntax details. Next, let's see what else we should pay attention to~

catalogue

On constructor

Constructor body assignment

Initialization list

Members that can only be initialized in the initialization list and precautions

explicit

static member

concept

Interview question: implement a class and calculate how many class objects are created in the program.

Characteristics

[question]

1. Can static member functions call non static member functions? may not

2. Can a non static member function call a static member function of a class? sure

3. Can static member functions call non static member variables? may not

4. Can non static member functions call static member variables of a class? sure

Summary

A new method of member initialization in C++11

Friends

friend function

Description of friend function:

Friend class

1. Friend relationship is unidirectional and not exchangeable.

2. Friend relationship cannot be transferred

Inner class

Concept and characteristics

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

An exercise on niuke.com

Understand encapsulation again

Object oriented again

On constructor

Constructor body assignment

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

Before that, we talked about the initialization of constructors 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;
};

Although there is an initial value in the object after the above constructor call, it cannot be called initialization of class object members. The statements in the constructor body can only be called initial value, not initialization. Because initialization can only be initialized once, and the constructor body can be assigned multiple times.

So how exactly is initialization represented in the constructor body? --- >

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.

Take chestnuts for example:

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

Members that can only be initialized in the initialization list and precautions

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
@ reference member variable

@ const member variable

@ custom type member (this class has no default constructor)

Take chestnuts for example:

class A
{
public:
	A(int a) //This constructor cannot be called without passing parameters. It is not the default constructor
		:_a(a)
	{}
private:
	int _a;
};
class B
{
public:
	B(int a, int ref)
		:_a(a)
		, _ref(ref)
		, _n(10)
	{}
private:
	A _a; // There is no default constructor
	int& _ref; // quote
	const int _n; // const
};

    A _a; // Because the constructor of a cannot be called without passing parameters, and it can only be initialized by assignment in the initialization list.


    int& _ ref; // Because the reference feature must be initialized when it is defined, it is declared rather than initialized here. The initialization place of the member} variable is initialized in the constructor initialization list. (initialize with another variable here)


    const int _n; // We know that the variable modified by const cannot be changed and cannot be assigned, so here_ N must be initialized at the time of definition. Here is declaration, not initialization. In order not to cause conflict, only one value can be initialized where the function initialization list is constructed.

3. 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 must use the initialization list first.


Let's demonstrate:

We found that the output of the above code is 100, so we can be sure_ Hour is post execution_ hour=100; , then it indicates that the initialization list is used for initialization first.

4. The declaration order of member variables in the class is the initialization order in the initialization list, which is independent of the order in the initialization list
Let's demonstrate through a scenario:

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

What will the above code output? Most people think it's output 1. Let's run it and see the results:

Is it incredible to think as usual. In fact, this is related to the initialization sequence.

private: / / member variable
    int _a2; / / declare first and initialize first
    int _a1; / / declared and initialized

A(int a) / / constructor
        :_ a1(a)  //_ After A1 is initialized, the transmitted a==1, so_ A1 is initialized to 1
        , _ a2(_a1)  //_ A2 is initialized first because_ A1 has not been initialized, so_ A1 is a random value, then_ A2 is also a random value
    {}

explicit

Constructors can not only construct and initialize objects, but also have the function of type conversion for constructors with single parameters.

Take chestnuts for example:

class Date
{
public:
	Date(int year)
		:_year(year)
	{}
private:
	int _year;
	int _month;
	int _day;
};
void TestDate()
{
	Date d1(2018);
	// Assign a value to a date type object with an integer variable
	// Behind the actual compiler, a temporary object will be constructed with 2019, and finally the temporary object will be used to assign a value to d1 object
	d1 = 2019;
}

The readability of the above code is not very good. Modifying the constructor with explicit will prohibit the implicit conversion of single parameter constructor. In fact, here, the compiler will set d1 = 2019; Optimize directly into a constructor. I will describe the details in other blogs ~ I hope you can support it.

static member

concept

Class members declared as static are called static members of the class, and member variables modified with static are called static member variables; The member function modified with static is called static member function. Static member variables must be initialized outside the class.

Interview question: implement a class and calculate how many class objects are created in the program.
 

class A
{
public:
	A() 
	{ 
		++_count; 
	}
	A(const A& t) 
	{ 
		++_count;
	}
	static int GetACount()
	{
		return _count; 
	}
private:
	static int _count;
};
int A::_count = 0; //The class domain needs to be specified for static member variable initialization
void TestA()
{
	cout << A::GetACount() << endl; //Call one of the static member function methods in the class
	A a1;
	A a2;
	A a3(a1);
	cout << A::GetACount() << endl;
}
int main()
{
	TestA();
	return 0;
}

The static global variable defined at this time is_ count. Note that initialization definitions can only be made outside the class.

When specified with A::GetACount(), the constructor inside will not be called again, and the value obtained is the final result. The following will be experienced through specific OJ questions~

Operation results:

Characteristics

1. Static members are shared by all class objects and do not belong to a specific instance
2. Static member variables must be defined outside the class without adding the static keyword, and the class field needs to be specified before the variable name
3. Class static members are available class names: static members or objects Static members to access (only static members can be accessed)
4. The static member function has no hidden this pointer and cannot access any non static member
5. Like ordinary members of a class, static members also have three access levels: public, protected and private, and can {have return values

[question]
1. Can static member functions call non static member functions? may not

Take chestnuts for example:

class Date
{
	Date(int year =1,int month =1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
	 static int CountDate()
	{
		 Print();
		_count++;
	}
private:
	int _year;
	int _month;
	int _day;
	static int _count;
};
int Date::_count = 0;
int main()
{
	return 0;
}

The operation is as follows:

We found that static member functions cannot call non static member functions, because static member functions do not have this pointer and cannot clearly indicate which object to call the function.  

2. Can a non static member function call a static member function of a class? sure

Take chestnuts for example:

class Date
{
public:
	Date(int year =1,int month =1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	void Print()
	{
		HelloPrint();
		cout << _year << " " << _month << " " << _day << endl;
	}
	 static void HelloPrint()
	{
		 cout << "hello world" << endl;

	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

Operation results:

Non static members can call static members.

3. Can static member functions call non static member variables? may not

Take chestnuts for example:

class Date
{
public:
	Date(int year =1,int month =1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	static void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

The operation is as follows:

If a static member function does not have this pointer, it cannot clearly indicate which object member variable to call.

4. Can non static member functions call static member variables of a class? sure

Take chestnuts for example:

class Date
{
public:
	Date(int year =1,int month =1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	 void Print()
	{
		cout << _count << endl;
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	static int _count;
};
int Date::_count = 0;
int main()
{
	Date d;
	d.Print();
	return 0;
}

Operation results:

Summary

Static member functions can only call static member functions or member variables.

Non static member functions can call both static member functions and member variables, as well as non static member functions and variables.

A new method of member initialization in C++11

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.

Take chestnuts for example:

class Date
{
public:
	void Print()
	{
		cout << _year << " " << _month << " " << _day;
	}
private:
	//Non static member variables can be given default values when members are declared
	int _year = 2022;
	int _month = 3;
	int _day = 3;
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

Let's print out to see if the default value we give is initialized for the member variable:

In this way, we find that we have not written the constructor, and the default value is assigned to the corresponding member variable.

This syntax scenario is applicable to only giving default values. It can be written in this way only to use the default constructor to assign initial values, so we don't have to write our own constructor.

If we write a copy construct (at this time, we must write a constructor. The specific reason has been described ~) but want to use the constructor generated by the compiler by default, we can define it as follows:

Date() = default;

This means that the display calls the constructor generated by the compiler by default.

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 (high cohesion, low coupling) and destroy the package, so friends should not be used more.

friend function

Problem: now we try to overload operator < < and find that we can't overload operator < < into member functions. Because the output stream object of cout and the implied 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 before it can be used normally. So we need to overload operator < < into a global function. But in this case, there will be no way to access members outside the class, so we need friends to solve it. The same goes for operator > >.

What does the above paragraph mean? Let's demonstrate with code to understand:

Look at the following code:

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	ostream& operator<<(ostream& _cout)
	{
		_cout << _year << "-" << _month << "-" << _day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d(2022, 3, 3);
	//cout << d;   Normal thinking logic
	d << cout; //However, definitions inside a class can only be used in this way
	return 0;
}

We found that the < < operator of the date class is overloaded, so we can output the date, but we know that d < < cout will be replaced with d.operator < < & D, _cout. At this time, this pointer receives & D as the left operator and cout as the right operator. At this time, cout can only be written to the right, but this is not in line with our habit.

How to solve it? If we define it outside the class, there will be no grab operator.

However, if we define it directly outside the class, we can't access the private members in the class, but we can't change the private members to public, which will destroy the original encapsulation. At this time, the friend function comes in handy:

class Date
{
	friend ostream& operator<<(ostream& _cout,const Date& d);
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout,const Date& d)//There is no problem of grabbing the position of this pointer
{
	_cout << d._year << "-" <<d. _month << "-" << d._day;
	return _cout; //This supports continuous output_ cin is similar
}
int main()
{
	Date d(2022, 3, 3);
	//cout << d;   Normal thinking logic
	cout << d; //However, definitions inside a class can only be used in this way
	return 0;
}

Operation results:

We just need to declare it with the friend + function in the class. This means that the function can access private and common member variables and functions in the class.

be careful:

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.

Description of friend function:

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


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

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

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. Friend relationship cannot be passed
If B is A friend of A and C is A friend of B, the friend of A at C cannot be explained.

1. Friend relationship is unidirectional and not exchangeable.

Take chestnuts for example:

class Date
{
	friend class Time;//Time is a friend class of Date. You can access the private member variables in Date
public:
	Date(int year = 2022, int month = 3, int day = 3)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
class Time
{
public:
	void Print(const Date& d)
	{
		cout << d._year << " " << d._month << " " << d._day;
		cout << endl;

		_d._year = 1;
		_d._month = 1;
		_d._day = 1;
		cout << _d._year << " " << _d._month << " " << _d._day; //This way of writing does not need to pass parameters
	}
private:
	int _hour;
	int _minute;
	int _second;
	Date _d;
};
int main()
{
	Date d;
	Time t;
	t.Print(d);
	return 0;
}

This is a friend class of Date, which can access the private member variable of Date.

Operation results:

2. Friend relationship cannot be transferred

For the above example, the time class defines friends in Date. Time can access the private members of Date, but Date cannot access the private members of Date, because Date does not define friend classes in the time class.

Take chestnuts for example:

class Time
{
private:
	int _hour = 12;
	int _minute = 12;
	int _second = 12;
};  //To ensure that Dtae calls Time, you need to define Time before Date or declare it
class Date
{
	friend class Time;
public:
	void Print(const Time& t)
	{
		cout << t._hour << ":" << t._minute << ":" << t._second << endl;
	}
};
int main()
{
	Date d;
	Time t;
	d.Print(t);
	return 0;
}

Note one detail: in order to ensure that Dtae calls Time, you need to define Time in front of Date or declare it.

However, when defining functions in a class, there is no need to consider the order or declare them in advance.

Let's run the above code to see if it can pass:

We can find that Date cannot access the private members in the Time class, and the friend class has only one-way transitivity.

Time class defines friends in Date. Time can access private members of Date, but Date cannot access private members of Date because Date does not define friend classes in time class.

Inner class

Concept and characteristics

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. All external parameters in the metaclass can be accessed through the internal parameters of the metaclass. But the outer class is not a friend of the inner class.

characteristic:
1. Internal classes can be defined in public, protected and private of external classes.
2. Note that the internal class can directly access the static and enumeration members in the external class without the object / class name of the external class.
3. Sizeof (external class) = external class, which has nothing to do with internal class.

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

class Date
{
public:
	Date(int year = 2022, int month = 3, int day = 3)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	class Time
	{
	public:
		void Print(const Date& d)
		{
			cout << d._year << " " << d._month << " " << d._day;
		}
	private:
		int _hour;
		int _minute;
		int _second;
	};
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d;
	cout << sizeof(d) << endl;
	Date::Time t;
	cout << sizeof(t) << endl;
	return 0;
}

Here, we should note that when defining an object of Time class, we must specify the external class domain, for example: Date::Time t;

When calculating the size of the external class, the internal class does not participate; When calculating the size of the internal class, the external class does not participate;

The operation results are as follows:

An exercise on niuke.com

Find 1 + 2 + 3 ++ n_ Niuke Tiba_ Niuke network

                        

code:

class Sum
{
public:
    Sum()
    {
        _ret += _i;
        _i++;
    }
    static int GetN()
    {
        return _ret;
    }
    static void Init()
    {
        _i = 1;
        _ret = 0;
    }
private:
    static int _i;
    static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
class Solution {
public:
    int Sum_Solution(int n) 
    {
        Sum::Init();
        Sum arr[n];//Sum* p = new Sum[n];
        return Sum::GetN();
    }
};

     class Solution {
public:
    int Sum_Solution(int n)
    {
        Sum::Init(); // In order to support multiple operations, the_ i and_ ret restores the initial value
        Sum arr[n];//Sum* p = new Sum[n]; Constructor executes n times
        return Sum::GetN();
    }
};

When we do not use static members:

            

return Sum().GetN()-(n+1); At this time, the sum () anonymous object calls the constructor again, which is equivalent to adding n + 1 and subtracting it at the end.

If you use return Sum::GetN(), the above problem will not exist (the constructor will not be called automatically this time, because the constructor is a non static member function).

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


Let's look at the railway station:
Ticketing system: responsible for ticketing - users enter with tickets and take their seats according to the number
Staff: ticket sales, consultation, security inspection, security, sanitation, etc
Train: take the user to the destination

With the cooperation of all the staff in the railway station, we can take the train in an orderly manner. We don't need to know the structure of the train and how the ticketing system operates, as long as it can be applied normally and conveniently.  

Think about it. What about an open platform without any management? There is no fence in the railway station, the management and dispatching of trains in the station are also random, and there are no rules for taking trains, such as:

Encapsulation is actually a more strict management.

Set what you want to access to others as public through the access qualifier, and set what you don't want to access to others as private or protected.

In this way, everyone uses it according to certain rules, which effectively reduces various problems of users.

Object oriented again

Object oriented understanding still needs us to feel it slowly.  

Topics: C++