[C++11] final and override keywords and new functions of classes

Table of contents

1. final and override keywords

1.1 final 

1.2 override

Second, the new function of the class

2.1 Default member functions

2.2 Class member variable initialization

2.3 default keyword

2.4 delete keyword


Note: All test codes in the C++ column are compiled and run in the environment of vs2019 

1. final and override keywords

These two keywords are used for inheritance and polymorphism

1.1 final 

final: Modifies the virtual function, indicating that the virtual function can no longer be rewritten

test code

//基类
class Person {
public:
	//被final修饰,该虚函数不能再被重写
	virtual void BuyTicket() final
	{ 
		cout << "Person-买票-全价" << endl; 
	}
};
//派生类
class Student : public Person {
public:
	//派生类的虚函数重写了父类的虚函数
	virtual void BuyTicket() { cout << "Student-买票-半价" << endl; }
};

compile directly error

The final keyword can also be used to implement a class that cannot be inherited  

How to implement a class that cannot be inherited? ?

  1. Make the constructor private, which is the practice of C++98
  2.  Add the final keyword when defining a class, which is the practice of C++11

 test code

//基类使用 final 修饰
class A final
{};
//派生类无法进行继承基类
class B : public A
{};

compile directly error

1.2 override

override:  Check whether the virtual function of the derived class overrides a virtual function of the base class, if not, compile and report an error

test code

//基类
class Person {
public:
	//基类的虚函数
	virtual void BuyTicket() { cout << "Person-买票-全价" << endl; }
};
//派生类
class Student : public Person {
public:
	//派生类完成了虚函数的重写,编译通过
	virtual void BuyTicket()override
	{ 
		cout << "Student-买票-半价" << endl; 
	}
};
//派生类
class Soldier : public Person
{
public:
	//派生类没有完成虚函数的重写,编译报错
	virtual void BuyTicket(int n)override
	{ 
		cout << "Soldier-优先-买票" << endl;
	}
};

 compile directly error

Second, the new function of the class

2.1 Default member functions

Before C++11, a class had the following six default member functions

  • 1. Constructor
  • 2. Destructor
  • 3. Copy constructor
  • 4. Copy assignment overloading
  • 5. Take address overload
  • 6. const address overloading

Last but not least are the first 4, the last two are of little use. The default member function is that if we don't write the compiler will generate a default

C++11 adds two new ones: move constructor and move assignment operator overloading

Build Conditions for Default Move Constructs

If you do not implement the move constructor yourself, and do not implement any of the destructor, copy construction, and copy assignment overloads. then the compiler will automatically generate a default move construct.

Default behavior for move construction: 

  • The move constructor generated by default will execute member-by-member byte-by-byte copy for built-in type members. For custom type members, you need to check whether the member implements move construction. If it is implemented, call move construction. If not, call copy construction 

Generate condition for default move assignment

If you do not implement the move assignment overload function yourself, and do not implement any of the destructor, copy construction, and copy assignment overloads, then the compiler will automatically generate a default move assignment.

Default behavior for move assignment:  

The move constructor generated by default will execute member-by-member byte-by-byte copy for built-in type members. For custom type members, you need to check whether the member implements move
assignment . (The default move assignment is exactly similar to the move construction above)

Note : If move construction or move assignment is provided, the compiler will not automatically provide copy construction and copy assignment

The test code needs to use a simplified version of the string implemented by the mock

namespace fy
{
	class string
	{
	public:
		//构造函数
		string(const char* str = "")
		{
			_size = strlen(str);//字符串大小
			_capacity = _size;//构造时,容量大小默认与字符串大小相同
			_str = new char[_capacity + 1];//为字符串开辟空间(多开一个用于存放'\0')
			strcpy(_str, str);//将C字符串拷贝到已开好的空间
		}	
		//拷贝构造 -- 现代写法
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			,_capacity(0)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);//复用构造函数,构造 tmp对象
			swap(tmp);//交换
		}
		//赋值重载 -- 现代写法1
		string& operator=(const string& s)
		{
			cout << "string& operator=(const string& s) -- 深拷贝" << endl;
			if (this == &s)//检查自我赋值
			{
				return *this;
			}
			string tmp(s);//复用拷贝构造函数,用s拷贝构造出对象tmp
			swap(tmp);
			return *this;//返回左值,目的是为了支持连续赋值
		}
		// 移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动语义" << endl;//更明显观察是否调用了该函数
			swap(s);//与右值的资源进行直接交换,不进行深拷贝
		}
		// 移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动语义" << endl;//使更明显观察是否调用了该函数
			swap(s);//与右值的资源进行直接交换,不进行深拷贝
			return *this;//支持连续赋值
		}
		//析构函数
		~string()
		{
			delete[] _str; //释放_str指向的空间
			_str = nullptr;
			_size = _capacity = 0;
		}
		//交换两个字符串
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

 Then write a simple Person class that uses the string we simulated

class Person
{
public:
	//构造函数
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
private:
	fy::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	Person s4;
	s4 = std::move(s2);
	return 0;
}

Note: The Person class here does not implement copy construction, assignment overloading, and destructors, but only implements constructors, which meet the generation conditions of move assignment and move copy functions

The result of the operation is as follows

 The default generated move construction and move assignment functions will perform member-by-member byte-by-byte copy for built-in type members. For custom type members, you need to check whether the member implements move
assignment Call copy assignment, which is fully consistent with

Test below

Once any one or more of the copy construction, copy assignment and destructor functions of the Person class are implemented, and the generation conditions of the move assignment and move copy functions are not satisfied, the move assignment and move copy functions will not be generated

test code

class Person
{
public:
	//构造函数
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	拷贝构造
	//Person(const Person& p)
	// :_name(p._name)
	// ,_age(p._age)
	//{}
	赋值重载
	//Person& operator=(const Person& p)
	//{
	//	if(this != &p)
	//	{
	//		_name = p._name;
	//		_age = p._age;
	//	}
	//	return *this;
	//}
	//析构函数
	~Person()
	{}
private:
	fy::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	Person s4;
	s4 = std::move(s2);
	return 0;
}

operation result

Since the default move assignment and move copy functions are not generated, the Person class can only call the deep copy function of string at this time, and the move assignment and move constructor cannot be called.

2.2 Class member variable initialization

C++11 allows initial default values ​​​​for member variables when defining a class. The default generated constructor will be initialized with these default values . It has been discussed in the chapter on classes and objects.

class Person
{
public:
	//...
private:
	//非静态成员变量,可以在成员声明时给缺省值
	string _name = "张三"; //缺省值
	int _age = 22;         //缺省值

	static int _n; //静态成员变量不能给缺省值
};

Note: This is not initialization, the value given is the default value

2.3 default keyword

The role of the default keyword is: to force the generation of default functions

C++11 gives you more control over which default functions to use. Suppose you want to use some default function, but for some reason this function is not generated by default. For example: if we provide a copy structure, the move structure will not be generated, then we can use the default keyword to display the generation of the specified move structure

Use: Add =default after the declaration that needs to be forced to generate a default function

test code

class Person
{
public:
	//构造函数
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	//拷贝构造
	Person(const Person& p)
	 :_name(p._name)
	 ,_age(p._age)
	{}
	//移动构造
	Person(Person&& p) = default;
private:
	fy::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

As a result of the operation, the default move construction can be generated after using the default keyword, even if the copy constructor is implemented

2.4 delete keyword

The role of the delete keyword is: to prohibit the generation of default functions 

Use: Add =delete after the statement that needs to prohibit the generation of default functions 

If you want to limit the generation of certain default functions:

  • In C++98, the function is set to private, and only declared without definition, so that an error will be reported as long as others want to call it.
  • It is simpler in C++11, just add =delete to the function declaration, this syntax instructs the compiler not to generate a default version of the corresponding function, and the function modified by =delete is called delete

Test code:

To prevent a class from being copied, you can use =delete to modify the copy construction and assignment of the class

class A
{
public:
	A()
	{}
private:
	//强制不允许生成
	A(const A&) = delete;
	A& operator=(const A&) = delete;
};

int main()
{
	A a1;
	A a2(a1);
	return 0;
}

compile error

----------------I am the dividing line---------------

The article is over here, the next one will be updated soon

Guess you like

Origin blog.csdn.net/m0_64280701/article/details/130197261