C++ classes and objects (initializer list, explicit keyword, static member, default declaration, friend, inner class)

First, the initialization list

In addition to the normal function, the constructor can also exist in the form of an 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 in parentheses.

1. Contrast with the functional form of the constructor

Data(int year,int month,int day)
{
    
    
 _year=year;
 _month=month;
 _day=day;
}

This is a type of constructor implemented earlier.

Data(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{
    
    }

This is the constructor that initializes the class table version.
Note: The initialization list is the place where member variables are defined, which is equivalent to being initialized at the time of definition.

2. Advantages of Initializer Lists

When defining a constructor, we recommend using the initialization list form to define the constructor, because in some special cases, the function form cannot be initialized.

class Data
{
    
    
private:
		int _year;
		int _month;
		int _day;
		const int _n;
public:
	Data(int year, int month, int day,const int n)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
		_n = n;
	}
};
int main()
{
    
    
	Data d1(2022, 1, 1,6);
}

When a member variable has a constant attribute, it cannot be initialized as above, because the constant attribute variable must be initialized when it is defined. The private type is just a declaration of it, and at this time we find that there is no chance to initialize it.
The initialization list is where the member variables are defined, so using the initialization list version of the constructor can initialize the constant variable.

class Data
{
    
    
private:
		int _year;
		int _month;
		int _day;
		const int _n;
public:
	Data(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
		,_n(1)
	{
    
    }
};
int main()
{
    
    
	Data d1(2022, 1, 1);
}

The initialization list can directly assign values ​​to constant attribute variables when it is defined, because the initialization list is where member variables are defined, and constant attribute variables can only be initialized at the place where they are defined.

3. The essential difference between the two definitions

The member variables in d1 are all defined when the class is defined, but the first method is equivalent to just defining the member variables, and the function needs to be called to initialize the member variables. The second way is equivalent to initializing them when defining the class. If we only want to initialize variables of constant properties when defining a class, we can also write:

Data(int year, int month, int day)
		:_n(10)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}

So the initialization list actually contains the first case of defining a constructor.
</font color=red>In fact, the definition of member variables occurs in the initialization list, but the first one is not written and it is not initialized.

4. Three cases of using initialization list

1. When there are constant attribute member variables.
2. When there are members defined by reference.
3. When there is a custom type member variable without a default constructor.

class A
{
    
    
private: int _b;
public:A(int a)
{
    
    
	_b = a;
}
};
class Data
{
    
    
private:
		const int _n;
		int& _ret;
		A _a;
public:
	Data(int a)
		:_n(10)
		,_ret(a)
		,_a(10)
	{
    
    }
};
int main()
{
    
    
	int i = 0;
	Data d1(i);
}

These three cases are initialized using the initialization list as above, but it should be noted that _ret is not a reference to i but a reference to a. Changing the value of i cannot change the value of _ret. If you want to change, you should use a reference to pass parameters.

5. Initialization sequence

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 their order in the initialization list.

class A
{
    
    
private: int _a2;
	     int _a1;
public:A(int a)
	:_a1(1)
	,_a2(_a1)
{
    
    }
};
int main()
{
    
    
	A a(1);
}

insert image description here

For example, in the above code, _a1 is assigned to 1 after execution, but _a2 is assigned to a random value.
This is because _a2 is declared earlier than _a1, so _a2 is initialized first, and then _a1 is initialized.

Second, the explicit keyword

1. Implicit type conversion of objects

Coercion also occurs when defining a class.

class Data
{
    
    
private:
	int _year;
	/*int _month;
	int _day;*/
public:
	Data(int year)
		:_year(year)
	/*	,_month(month)
		,_day(day)*/
	{
    
    }
};
int main()
{
    
    
	Data d1(2022);
	d1 = 10;
}

</font color=blue>This is an implicit type conversion, which means that first construct a temporary object with 10, and then use this object to copy and construct d1.
Although a total of two copy constructions occur, the copy constructor is called only once, which is an optimization made by the compiler for convenience.

We found that a total of two functions are called here, one is the constructor and the other is the copy construction. When the compiler optimizes, the copy construction is optimized away.

2.explicit

Expilcit prevents implicit type conversions of objects from taking place.

explicit Data(int year)
		:_year(year)
	{
    
    }

In this way, when d1=20 again, the compiler will report an error.

Three, static members

1. Concept

A class member declared as static is called a static member of the class,
a member variable decorated with static is called a static member variable;
a member function decorated with static is called a static member function.
Static member variables must be initialized outside the class.

2. Features

1. Static members are shared by all class objects.
2. Static member variables must be defined outside the class without the static keyword. But non-static members cannot be defined outside the class.
3. Class static members can be accessed using class name::object.static members.
4. Static member functions have no hidden this pointer and cannot access any static members.
5. Like ordinary members of a class, static members also have three levels of access such as public, and can also have return values.

3. Application

The simplest application, we can determine how many objects a class has constructed.

class Data
{
    
    
private:
	int _year;
	int _month;
	int _day;
	static int count;
public:
	static int Getcount()
	{
    
    
		return count;
	}
	explicit Data(int year=1,int month=1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{
    
    
		count++;
	}
};
int Data::count = 0;
int main()
{
    
       
	Data d1(2022,1,1);
	Data d2;
	Data d3;
	cout << d1.Getcount() << endl;
}

Here count is a static member variable and Getcount is a static member function.
The count in the class is just a declaration, and the definition of count should take place outside the class. count does not belong to a certain object, it belongs to the global scope of an object's change to count is a permanent change. There are two access methods for count, which can be accessed directly in the class domain or in the object.

Data::count=1;
d1.count=1;

Both effects are the same.
insert image description here
At the same time, static member functions cannot access member variables because there is no this pointer.

static int Getcount()
	{
    
    
	_year=1;//错误,不能访问成员变量
		return count;
	}

There are also two ways to call static member functions:

d1.Getcount();
Data::Getcount()

</font color=red>Only static member functions or variables can be called using Data::, and ordinary members can only be called using the object name.

Fourth, the default statement

To circumvent compiler default constructors that cannot initialize built-in types, C++11 introduced default declarations.

class Data
{
    
    
private:
	int _year = 1;
	int _month = 2;
	int _day = 3;//缺省声明
public:
	void Print()
	{
    
    
		cout << _year << " " << _month << " " << _day << endl;
	}
};
int main()
{
    
      
	Data d1;
	d1.Print();
}

When the constructor does not assign a value to a member variable, the member variable uses the default value.
insert image description here
</font color=red>Note: Static member variables cannot be given default values ​​and must be initialized in a global location outside the class

Five, Tomomoto

1. Friend function

(1) Example

The friend function was actually mentioned in the previous section, and we still refer to the previous example:

class Data
{
    
    
	friend void operator<<(ostream& out, const Data& d);
private:
	int _year;
	int _month;
	int _day;
public:
	Data(int year = 0, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
};
void operator<<(ostream& out, const Data& d)
{
    
    
	out << d._year<<" " << d._month << " " << d._day;
}
int main()
{
    
    
	Data d1(2022, 1, 3);
	Data d2(2022, 3, 15);
	cout << d1;
}

Here, in operator overloading, in order to make the function operator<< call members _year in the class, etc., a friend function is used, that is, a line of declaration is added to the class:

friend void operator<<(ostream& out, const Data& d);

In this way, the function can freely use the private member variables in the class.

(2) Description

1. Friend functions can access the private and protected members of the class, but cannot access the member functions of the class .
2. Friend functions cannot be modified with const.
3. Friend functions can be declared anywhere in the class definition, regardless of access qualifiers .
4. A function can be a friend function of multiple classes.
5. The call of friend function is the same as that of ordinary function.

2. Tomomoto

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

1. Friendship is one-way and not commutative.
2. Friendship cannot be passed.

class A
{
    
    
	friend class B;
private:
	int _a;
	B b;
public:
	void fun1()
	{
    
    
		b._b = 1;//会报错,无法访问到B中的私有成员
	}
};
class B
{
    
    
private:
	int _b;
	A a;
public:
	void fun2()
	{
    
    
		a._a = 1;
	}
};

</font color=red> Declare friend class B in class A, which means that member functions in B can access private members in A, but member functions in A cannot access private members in B

6. Internal class

1. Concept

If a class is defined inside another class, the inner class is called an inner class. Note that the inner class is a separate class at this point.
It does not belong to the outer class, and it cannot call the inner class through the object of the outer class. Outer classes do not have any privileged access rights to inner classes.
</font color=pink>The inner class is the friend class of the outer class. The inner class can access all members of the outer class through the object parameter of the outer class, but the outer class is not a friend of the inner class.

2. Features

1. The inner class can be defined in the outer class, public, private, protected are all possible.
2. Note that the inner class can directly access the static and enumeration members in the outer class, without the object or class name of the outer class.
3.sizeof(outer class) = outer class, which has nothing to do with inner class.

class A
{
    
    
private:
	static int k;
	int h;
public:
	class B
	{
    
    
		void f(const A& a)
		{
    
    
			cout << k << endl;
			cout << a.h << endl;
		}
		void Print()
		{
    
    
			cout << 1 << endl;
		}
	};
};
int A::k = 1;
int main()
{
    
    
	A::B b;
	return 0;
}

At this time, B is an inner class of A, and B can directly access the static members in A, and can also access the private variables in A.
</font color=red>Note: When using the inner class B to define an object, it needs to be added in the A domain, A::B b, and b cannot access the member functions in B at this time.

7. Summary

So far, the summary of classes and objects ends here. Classes and objects are an important part of object-oriented programming. C++ combines the attributes and behaviors of an object through classes. Make it more in line with people's cognition of one thing, package all the things that belong to the object, selectively open up some of its functions to interact with other objects through access qualifiers, and for some internal implementations of the object The details, external users do not need to know, it is useless to know some situations, but it increases the difficulty of use or maintenance, and complicates the whole thing.

Guess you like

Origin blog.csdn.net/qq_51492202/article/details/122656438