C++ primary classes and objects (3)

1. Talk about the constructor again

(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, 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 the initialization of the member variables in the object. The statement in the constructor body can only be called the initial value assignment, not called initialization. **
Because the initialization can only be initialized once, and the constructor body can be assigned multiple times.

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

 
class Date
{
    
    
	friend ostream& operator<<(ostream& out, const Date& d)
	{
    
    
		out << d._year << "/" << d._month << "/" << d._day << endl;
		return out;
	}
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{
    
    }
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date date(1, 1, 1); 
	cout << date;
	return 0;
}

【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:

  • reference member variable
  • const member variable
  • A custom type member (and the class has no default constructor)
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; //没有默认构造函数
	int& _ref; //引用
	const int _n; //const
};

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

class Time
{
    
    
public:
	Time(int hour = 0)
		:_hour(hour)
	{
    
    
		cout << "Time()" << endl;
	}
private:
		int _hour;
};
class Date
{
    
    
public:
	Date(int day)
	{
    
    }
private:
	int _day;
	Time _t;
};
int main()
{
    
    
	Date d(1);
	return 0;
}

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

The output of the following program:
A. Output 1 1
B. Program crashes
C. Compilation fails
D. Output 1 random value

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

insert image description here

(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
{
    
    
	friend ostream& operator<<(ostream& out, const Date& d)
	{
    
    
		out << d._year << "/" << d._month << "/" << d._day << endl;
		return out;
	}
public:
	// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
	Date(int year)
	{
    
    
		_year = year;
		_month = 0;
		_day = 0;
	}
	// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
	//有类型转换作用
	//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 date = 100;
	cout << date;
	return 0;
}
int main()
{
    
    
	//类型转换
	Date date = 100;
	cout << date;
	return 0;
}

insert image description here
Multi-argument conversion (C++11)

In C++11, multi-parameter type conversion is supported.

class Date
{
    
    
	friend ostream& operator<<(ostream& out, const Date& d)
	{
    
    
		out << d._year << "/" << d._month << "/" << d._day << endl;
		return out;
	}
public:
	
	Date(int year,int month,int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
 
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
    
    
	//类型转换
	Date date = {
    
    100,100,100};
	cout << date;
	return 0;
}

insert image description here
If you add the explicit keyword:

class Date
{
    
    
	friend ostream& operator<<(ostream& out, const Date& d)
	{
    
    
		out << d._year << "/" << d._month << "/" << d._day << endl;
		return out;
	}
public:
	// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
	explicit Date(int year)
	{
    
    
		_year = year;
		_month = 0;
		_day = 0;
	}
	// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
	//有类型转换作用
	//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 date = 100;
	cout << date;
	return 0;
}

insert image description here

The explicit modifier constructor prohibits type conversion.

Two. static members

Class members declared 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.

Interview question: Implement a class and calculate how many class objects are created in the program.
Analysis: To create a class object, either call the constructor or call the copy constructor, use a counter to count the number of calls to the constructor and copy construction, and you can find out how many class objects have been created in total. We can use global variables To count, you can also use static static member variables to count.

class A
{
    
    
public:
	A() 
	{
    
    
		++_scount; 
	}
	A(const A & t) 
	{
    
    
		++_scount; 
	}
	static int GetACount() 
    {
    
     
        return _scount; 
    }
private:
	static int _scount;
};
//静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。
int A::_scount = 0;
A Fun(A a4)
{
    
    
	A tmp(a4);
	return tmp;
}
int main()
{
    
    
	cout << A::GetACount() << endl;
	A a1, a2;
	A a3(a1);
	Fun(a3);
	cout << A::GetACount() << endl;
	return 0;
}

insert image description here

(1) static feature

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

class A
{
    
    
public:
	static int GetACount() {
    
     return _scount; }
	static int _scount;
};
int A::_scount = 0;
A Fun(A a4)
{
    
    
	A tmp(a4);
	return tmp;
}
int main()
{
    
    
	A* ptr = nullptr;
	//因为静态成员变量不属于对象,我们通过空指针访问也不会有问题。
	cout << ptr->_scount<< endl;
	return 0;
}

insert image description here
2. Static member variables must be defined outside the class. The static keyword is not added when defining, and only declared in the class.
3. Class static members can be accessed with class name::static member or object.static member.

class A
{
    
    
public:
	static int GetACount() {
    
     return _scount; }
	static int _scount;
};
int A::_scount = 0;
A Fun(A a4)
{
    
    
	A tmp(a4);
	return tmp;
}
int main()
{
    
    
	A* ptr = nullptr;
	A a;
	
	cout << ptr->_scount<< endl;
	cout << A::_scount << endl;
	cout << a._scount << endl;
	return 0;
}

insert image description here
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, subject to the restrictions of public, protected, and private access qualifiers.

[Questions]

1. Can a static member function call a non-static member function?

Answer:
Direct access is not possible. Because the object's this pointer is missing.

Is it okay if we manually add the object pointer parameter? Manually adding object pointer parameters can not only access non-static member functions, but also non-static member variables.

class A
{
    
    
public:
	void show()
	{
    
    
		cout << "void show()" << endl;
	}
	static int GetACount(A*a) 
	{
    
    
		a->show();
		cout << a->n << endl;
		return _scount; 
	}
	static int _scount;
	int n = 100;
};
int A::_scount = 0;
int main()
{
    
    
	A a;
	a.GetACount(&a);
	return 0;
}

insert image description here

  1. Can a non-static member function call a static member function of a class?
    Answer:
    Yes.
class A
{
    
    
public:
	void show()
	{
    
    
		cout << "void show()" << endl;
		GetACount();
	}
	static int GetACount() 
	{
    
    
		cout << "static int GetACount()" << endl;
		return _scount; 
	}
	static int _scount;
	int n = 100;
};
int A::_scount = 0;
int main()
{
    
    
	A a;
	a.show();
	return 0;
}

insert image description here

Three. Friends

Friends provide a way to break out of encapsulation, and sometimes convenience. But friends will increase the degree of coupling and destroy the encapsulation, so friends should not be used more often.
Friends are divided into: friend function and friend class .

(1) Friend function

We tried to overload operator<< before, and found that there is no way to overload operator<< into a member function. Because the output stream object of cout 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. But in actual use, cout needs to be the first formal parameter object to be used normally. So overload operator<< into a global function. But it will cause no way to access members outside the class, and friends are needed to solve it.
operator>>The same reason.

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

insert image description here
illustrate:

1. Friend functions can access private and protected members of a class, but not member functions of a class.
Because the friend function itself is an external function.

2. Friend functions cannot be modified with const.
Because friend functions are non-member functions, they cannot be used, and they are modified by const, because const is modified by *this.

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 principle of calling a friend function is the same as that of a normal function.

(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
{
    
    
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;
	}
	void Show()
	{
    
    
		cout << _year << "/" << _month << "/" << _day 
			<< " " << _t._hour << ":" << _t._minute << ":" << _t._second << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
int main()
{
    
    
	Date d(2023, 2, 11);
	d.SetTimeOfDate(10, 0, 0);
	d.Show();
	return 0;
}

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.

3. The friendship relationship cannot be inherited, and I will give you a detailed introduction in the inheritance position.

4. Inner class

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, let alone 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.

Note: The inner class is the friend class of the outer class. See 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 outer class is not a friend of the inner class.

class A
{
    
    
private:
	static int k;
	int h;
public:
	class B // B天生就是A的友元
	{
    
    
	public:
		void foo(const A& a)
		{
    
    
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	};
};
int A::k = 1;
int main()
{
    
    
	A::B b;
	b.foo(A());
	return 0;
}

insert image description here

Explanation: Why is h in class a here 0, because we have not instantiated type A, so h in class a at this time is equivalent to a global variable. When global variables are not initialized, they default to 0.

characteristic:

  • The inner class can be defined in the public, protected, and private of the outer class.
  • Note that the inner class can directly access the static members in the outer class without the object/class name of the outer class.
  • sizeof(outer class) = outer class, has nothing to do with inner class.

5. Anonymous objects

The characteristic of an anonymous object is that it does not need to take the name of the object, but its life cycle only has this line.

class Solution {
    
    
public:
	int Sum_Solution(int n) {
    
    
		//...
		return n;
	}
};
int main()
{
    
    
	cout << Solution().Sum_Solution(10) << endl;
	return 0;
}

We can chain access to members of a class without explicitly creating the object.

6. Some compiler optimizations when copying objects

Next, we focus on the optimization of the compiler when copying. Our compiler optimization here is mainly VS2022.

In the process of passing parameters and returning values, generally the compiler will do some optimizations to reduce the copying of objects, which is still very useful in some scenarios.

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;
};
void f1(A aa)
{
    
    }
A f2()
{
    
    
	A aa;
	return aa;
}
int main()
{
    
    
	// 传值传参,先构造,再拷贝构造。
	A aa1;
	f1(aa1);
	cout << endl;
	// 传值返回,先构造,再拷贝构造。
	f2();
	cout << endl;
	// 隐式类型,未优化:先构造,再拷贝构造 -> 优化为直接构造。
	f1(1);
	// 一个表达式中,先构造,再拷贝构造 -> 优化为一个构造。
	f1(A(2));
	cout << endl;
	// 一个表达式中,先构造,再拷贝构造,再拷贝构造 ->优化为 构造+拷贝构造。
	A aa2 = f2();
	cout << endl;
	// 一个表达式中,先构造,再拷贝构造,再赋值重载 ->无法优化。
	aa1 = f2();
	cout << endl;
 
	return 0;
}

insert image description here

Guess you like

Origin blog.csdn.net/weixin_47952981/article/details/129133419