C++ classes and objects (6)

1. Talking about the constructor

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, int month, int day)//构造函数
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}

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

Although there is already an initial value in the object after the above constructor is called, it cannot be called initialization of member variables in the object, and the statement in the constructor body can only be called initial value assignment , not 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.

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

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

【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
  • custom type member type (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
};
  1. Try to use initialization list initialization, because whether you use initialization list or not, for custom type member variables, it must be initialized with initialization list first.
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);
}
  1. 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.
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();
}

//A.输出1  1
//B.程序崩溃
//C.编译不通过
//D.输出1  随机值 right

1.3. The explicit keyword

Constructors can not only construct and initialize objects, but also have the function of type conversion for one parameter or constructors with default values ​​except for the first parameter.

// 单参数的构造函数
class A
{
    
    
public:
	explicit A(int a)
		:_a1(a)
	{
    
    
		cout << "A(int a)" << endl;
	}

	//explicit A(int a1, int a2)
	A(int a1, int a2)
		:_a1(a1)
		, _a2(a2)
	{
    
    }

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

private:
	int _a2;
	int _a1;
};

int main()
{
    
    
	// 单参数构造函数 C++98
	A aa1(1);  // 构造函数
	//A aa2 = 1; // 隐式类型转换   构造+拷贝+优化->构造
	//const A& ref = 10;


	// 多参数构造函数 C++11
	A aa2(1, 1);
	A aa3 = {
    
     2, 2 };
	const A &ref = {
    
     2, 2 };

	int i = 1;
	double d = i; // 隐式类型转换

	return 0;
}
class Date
{
    
    
public:
	// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
	explicit Date(int year)
	//Date(int year)
		:_year(year)
	{
    
    }
	/*
	// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
	有类型转换作用
	// explicit修饰构造函数,禁止类型转换
	explicit Date(int year, int month = 1, int day = 1)
	: _year(year)
	, _month(month)
	, _day(day)
	{}
	*/
	Date& operator=(const Date& d)
	{
    
    
		if (this != &d)
		{
    
    
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
void Test()
{
    
    
	Date d1(2022);
	// 用一个整形变量给日期类型对象赋值
	// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
	d1 = 2023;
	// 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转
	//换的作用
}

int main()
{
    
    
	Test();
	return 0;
}


insert image description here

The readability of the above code is not very good, modifying the constructor with explicit will prohibit the implicit conversion of the constructor.

2. static members

2.1. Concept

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.

【method 1】

//方法1:全局变量统计
int count = 0;
using  std::cout;
using std::endl;
class Date
{
    
    
public:
	Date(int year=0)
		:_year(year)
	{
    
    
		++count;
	}
	Date(const Date& d)
		:_year(d._year)
	{
    
    
		++count;
	}

private:
	int _year;
};

int main()
{
    
    
	Date d1;
	Date d2(2023);
	Date d3 = d1;
	cout << count << endl;
	return 0;
}

Disadvantages of defining global variables:

  1. Global variables are easy to cause pollution of variable naming and duplication of naming
  2. It is not safe. In addition to changing the value of count when constructing and copying constructor calls, the value of count can also be changed in other places, resulting in inaccurate results.

【Method 2】

//方法2:static成员变量进行统计
class Date
{
    
    
public:
	Date(int year = 0)
		:_year(year)
	{
    
    
		++_count;
	}
	Date(const Date& d)
		:_year(d._year)
	{
    
    
		++_count;
	}

	static int GetCount()
	{
    
    
		return _count;
	}

private:
	int _year;
	static int _count;
};
int Date::_count = 0;

int main()
{
    
    
	Date d1;
	Date d2(2023);
	Date d3 = d1;
	cout << Date::GetCount() << endl;
	return 0;
}

Points to note when using static member variables:

  1. Initialization should be in the global
  2. In order to solve the inability to access private member variables outside the class, a function must be implemented to get the value of _count
  3. In order to avoid getting the value of _count, a class must be defined, and the function should be set to a static type. Outside the class, the function can be directly accessed through the class name.

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, 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
  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 a class, restricted by public, protected, and private access qualifiers

3. Friend function

Problem: Now try to overload operator<<, and then find 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.

class Date
{
    
    
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{
    
    }
	// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
	ostream& operator<<(ostream& _cout)
	{
    
    
		_cout << _year << "-" << _month << "-" << _day << endl;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

A friend function can directly access the 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 is 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;
}

【illustrate】

  • Friend functions can access private and protected members of a class, but not member functions of the class
  • Friend functions cannot be modified with const
  • Friend functions can be declared anywhere in the class definition, not restricted by class access qualifiers
  • A function can be a friend function of multiple classes
  • The principle of calling a friend function is the same as that of a normal 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.

  • The friendship relationship is one-way and not interchangeable.
    For example, the above-mentioned Time class and Date, if you 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 want to It is not possible to access private member variables in the Date class in the Time class.
  • Friend relationship cannot be transmitted
    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 relationship cannot be inherited
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, 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, and 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. The inner class can be defined in the public, protected, and private of the outer class.
  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(outer class) = outer class, has nothing to do with 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;
}

5. Anonymous objects

insert image description here

6. Some compiler optimizations when copying objects

insert image description here
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=0)" << 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)
{
    
    }

void f2(const A& aa)
{
    
    }

A f2()
{
    
    
	A aa;
	return aa;
}

int main()
{
    
    
	// 传值传参
	A aa1;
	f1(aa1);
	cout <<"-------------------"<< endl;
	//传引用传参
	A aa2;
	f2(aa2);
	cout << "-------------------" << endl;
	// 隐式类型,连续构造+拷贝构造->优化为直接构造
	f1(1);
	cout << "-------------------" << endl;
	// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
	A aa3 = f2();
	cout << "-------------------" << endl;
	// 一个表达式中,连续拷贝构造+赋值重载->无法优化
	aa1 = f2();
	cout << endl;


	cout << "-------------------" << endl;
	return 0;
}

insert image description here
insert image description here
insert image description here

7. Understand classes and objects again

A class is a description of a certain type of entity (object), describing the properties and methods of the object, and a new custom type is formed after the description is completed, and the specific type can be instantiated with this custom type Object.

Guess you like

Origin blog.csdn.net/zxj20041003/article/details/130543470