C++: static members of classes, friends and constructor initialization lists

Table of contents

1. The initialization list of the constructor of the class

1. The introduction and introduction of the constructor initialization list of the class

2. The initialization list is used to call the copy constructor of the class object member of the class

3. The usage rules of the initialization list

4. A note on using initialization lists 

Two. explicit keyword

3. Static members of C++ classes

1. Static modified member variables in the class

2. Static modified member functions in the class

3. Related exercises

4. Friend functions and friend classes of classes

1. Friend function of class

2. Friend class of class

5. Some compiler optimizations when copying objects


Chapter structure:

 

1. The initialization list of the constructor of the class

1. The introduction and introduction of the constructor initialization list of the class

  • When a class object is created, the definition of its member variables (opening and initialization of member variable memory space) is completed before the constructor function body is executed :

for example:

class Date
{
public:
	Date(int year = 0,int day=0)     Date的构造函数
	{
		cout << "pause" << endl;

		_year = year;
		_day = day;
	}

private:
	int _year;
	int _day;
};


int main()
{
	Date a;
	return 0;
}

  • Therefore, the statement in the constructor body is only to assign values ​​to member variables , not to initialize member variables ( initialization of variables refers to assigning initial values ​​to variables immediately after applying for memory space ) ( initialization can only be done during the life cycle of variables Once, and the constructor body can be assigned multiple times)
  • If there are const member variables (readable, non-writable variables) in the class, the constructor cannot initialize them.

for example:


 

Code snippet fails to compile.

In order to solve this problem, C++ designed the initialization list of the constructor, which is used to initialize the member variables ( immediately assign the initial value to the member variables after opening up the memory space ).

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

for example:

class Date
{
public:
	Date(int year = 0, int month = 0, int day = 0)
		: _year(year)                   成员变量的初始化列表
		, _month(month)
		, _day(day)
		, _Cday(3)                      const成员变量的初始化
	{                                   构造函数函数体
		cout << "pause" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	const int _Cday;
};


int main()
{
	Date a;
	return 0;
}

 

2. The initialization list is used to call the copy constructor of the class object member of the class

About the copy constructor of the class: http://t.csdn.cn/HNeit

Some compilers (such as the VS series) will not add instructions to call the copy constructor of its class object member variable for the custom copy constructor of the class , such as:
#include <iostream>

using std::cout;
using std::cin;
using std::endl;

class subdate
{
public:
	subdate()								类的构造函数
		:_a(0)
	{
		cout << "subconstructor" << endl;
	}
	subdate(subdate& date)                  类的拷贝构造函数
	{
		cout << "copysubdate" << endl;
	}

private:
	int _a;
};

class Date
{
public:

	Date(int year = 0, int month = 0, int day = 0)   类的构造函数
		: _year(year)
		, _month(month)
		, _day(day)
		, _Cday(3)
		,_test()
	{
		cout << "constructor" << endl;
	} 
	Date(Date & date)								 类的自定义拷贝构造函数
		: _Cday(3)
	{
		cout << "Dateconstructor" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
	const int _Cday;
	subdate _test;

};


int main()
{
	Date a;

	Date b(a);               对象b的创建会调用拷贝Date的自定义拷贝构造函数
	return 0;
}

Graphical analysis:

If you want the custom copy constructor of a class to call the copy constructor of the class object member of the class , we must explicitly indicate the call of the copy constructor of the class object member in the initialization list of the copy constructor .

That is, modify the custom copy constructor of the Date class as follows:

	Date(Date & date)								 //类的自定义拷贝构造函数
		: _Cday(3)
		,_test(date._test)
	{
		cout << "Dateconstructor" << endl;
	}

3. The usage rules of the initialization list

  1. Each member variable can only appear once  in the initialization list ( because initialization can only be initialized once )
  2. The class contains the following members, which must be placed in the initializer list for initialization :
  • reference member variable
  • const member variable
  • Class object member variables ( especially when the class has no constructor that can be called without parameters )

for example:

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成员变量
};

The initialization list allows the system to assign initial values ​​to member variables while opening up memory space for class member variables , and also allows us to explicitly call the constructor of the class object member in the class in the custom constructor .

Therefore, in any case , we should try our best to use the initialization list to initialize member variables in the constructor of the class

(The initialization order of member variables is best consistent with the declaration order of member variables)

4. A note on using initialization lists 

The order in which member variables of a class are declared in the class definition is the order in which they are initialized in the initialization list , regardless of the order in which they are written in the initialization list .
for example:

class A
{
public:
	A(int a=1)
		:_a1(a)
		, _a2(_a1)
	{}
	void Print() 
    {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};

int main() 
{
	A aa(1);
	aa.Print();
	return 0;
}

Therefore, when the aa object in the code segment is created, _a2 will be assigned a random value ( _a2 will be initialized first, and the _a1 space will still store a random value when _a2 is initialized )

Two. explicit keyword

There are the following ways to create class objects in C++:

class Date
{
public:
	Date(int year = 0)     构造函数
	{
		_year = year;
        cout << "constructor"<<endl;
	}
    Date(Date& date)       拷贝构造函数
    {
        _year = date._year;
        cout << "copy"<<endl;
    }

private:
	int _year;
};

int main() 
{
	Date a = 2022;         该代码语句发生了隐式类型转换
	return 0;
}

An implicit type conversion occurs in the code snippet: 

However, this way of writing is easy to cause misunderstanding, so you can add the explicit keyword before the constructor of the class to prohibit the occurrence of this implicit type conversion.

3. Static members of C++ classes

1. Static modified member variables in the class

Characteristics of static modified member variables (static member variables):

  • The life cycle of static member variables is the same as that of global variables (created when the program starts and destroyed when it ends)
  • A static member variable does not occupy the memory space of any class object ( its existence does not depend on any object ) , it is stored independently in the static area , and all class objects of this class can access the static member variable
  • Static member variables must be defined outside the class , without adding the static keyword but to indicate which class domain it belongs to.
  • Static member variables can be accessed with class name::static member or object.static member

for example:

2. Static modified member functions in the class

Features of static modified member functions (static member functions):

  • Class static member functions can be accessed with class name::static member function or object.static member function
  • There is no hidden this pointer in the formal parameters of static member functions , and any non-static member variables cannot be directly accessed
  • Static member functions cannot directly call non-static member functions
  • Non-static member functions can directly call static member functions of the class
     

for example:

3. Related exercises

Niuke.com JZ64 Seeking 1+2+3+...+n 

OJ link: seek 1+2+3+...+n

describe

To find 1+2+3+...+n, it is required not to use keywords such as multiplication and division, for, while, if, else, switch, case, and conditional judgment statements (A?B:C).

Data range: 0<n≤200
Advanced: Space complexity O(1), time complexity O(n)

You can solve this problem with the help of static members of the class:

class Ans
{
public:
    Ans()          每调用一次构造函数就累加一次
    {
        i++;       两个静态变量的生命周期为全局变量的生命周期
        ret+=i;
    }
    int Getans ()
    {
        return ret;
    }
private:
    static int i;
    static int ret;
};
int Ans::i =0;
int Ans:: ret =0;

class Solution 
{
public:
    int Sum_Solution(int n) 
    {
        Ans* ptr = new Ans[n-1];
        Ans ans;          一共创建了n个Ans对象,调用了n次Ans的构造函数
        return ans.Getans();
    }
};

4. Friend functions and friend classes of classes

1. Friend function of class

The concept of a class friend function:

A friend function is an ordinary function defined outside a class and does not belong to any class, but it needs to be declared inside the class, and the keyword friend needs to be added when declaring .

  • Friend functions can directly access private and protected members of a class , but not member functions of a 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

For example, now we overload two operators outside the class:

#include <iostream>

using std::endl;
using std::istream;
using std::ostream;


class Date
{
public:
	friend void operator>>(istream& cin, Date& date);    在类中声明类的友元函数
	friend void operator<<(ostream& cout, Date& date);
private:
	int _year;
};

void operator>>(istream& cin, Date& date)                重载>>运算符
{
	cin >> date._year;
	std::cout << "operator>>" << endl;
}

void operator<<(ostream& cout, Date& date)               重载<<运算符
{
	cout << date._year << endl;
	std::cout << "operator<<" << endl;
}

int main()
{
	Date a;
	std::cin >> a;                                        调用重载的>>运算符
	std::cout << a;                                       调用重载的<<运算符
	return 0;
}

2. Friend class of class

class Time
{
friend class Date;    声明Date类为时间类的友元类,则在Date类中就可以直接访问Time类
                      中的私有成员变量
public:
            
private:
    int _hour;
    int _minute;
    int _second;
};


class Date
{
public:
            
    void SetTimeOfDate(int hour, int minute, int second)
    {     
        _t._hour = hour;              直接访问时间类私有的成员变量
        _t._minute = minute;
        _t._second = second;
    }
private:
    Time _t;

};

The basic concept of class 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 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 .

  • Friendship cannot be passed

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


 

Friends provide a way to break out of encapsulation, and sometimes convenience. But friends will increase the degree of coupling and break the encapsulation, so if you can use friends, you don’t need them. 

5. Some compiler optimizations when copying objects

Given a piece of code:

class A
{
public:
	A(int a = 0)                        A的构造函数
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)                      A的拷贝构造函数
		:_a(aa._a) 
	{
		cout << "A(const A& aa)" << endl;
	}
	A& operator=(const A& aa)           A的赋值运算符重载
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}                        
	~A()                                A的析构函数
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};


void f1(A aa)
{
	;
}


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



int main()
{
	A a = f2();
	cout << endl;
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_73470348/article/details/128765955