文章目录
- 类的新功能
- 默认成员函数
- 类成员变量初始化
- 强制生成默认函数的关键字default
- 禁止生成默认函数的关键字delete
- 继承和多态中的final和override关键字
类的新功能
默认成员函数
原来在C++类中,有6个默认成员函数:
1: 构造函数
2: 拷贝构造函数
3: 拷贝赋值重载
4: 析构函数
5: 取地址重载
6: const取地址重载
最后重要的是前4个,后两个用处不大,默认成员函数就是我们不写编译器会生成一个默认的,但是C++11中新增了两个默认成员函数:移动构造函数和移动赋值运算符重载.
移动构造函数和移动赋值运算符重载有一些需要注意的点
( 1 ) : 如果我们没有实现移动构造函数,且没有实现析构函数,
拷贝构造,拷贝赋值重载中的任意一个(意味着没有实现深拷贝).那么编译器会主动生动生一个默认移动构造.默认生成的移动构造函数:
- 对于内置类型成员会执行逐成员按字节拷贝,
- 对于自定义类型成员,如果该成员实现了移动构造就主动调用移动构造.
( 2 ): 如果我们实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值:
- 对于内置类型成员会执行逐成员按字节拷贝.
- 对于自定义类型成员,则需要看这个成员是否实现移动赋值如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似).
( 3 ): 如果我们提供了移动构造和移动赋值,编译器就不会主动提供拷贝构造和拷贝赋值.
如果我们需要对上述结论进行验证,我们则需要使用到以前模拟实现的string类.
namespace yzh
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
//构造函数
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
//cout << "string(char* str)" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//构造函数(现代写法)
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 拷贝构造
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 深拷贝" << endl;
string tmp(s._str);
swap(tmp);
}
// 赋值重载
string& operator=(const string& s)
{
cout << "string& operator=(string s) -- 深拷贝" << endl;
string tmp(s);
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 = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
//string operator+=(char ch)
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
private:
char* _str;
size_t _size;
size_t _capacity; // 不包含最后做标识的\0
};
}
此外,我们还需要一个简单的Person类,其中Person类中包含着一个自定义成员Date;
class Date
{
public:
Date()
:_a(0)
{
}
Date( const Date& de)
:_a(de._a)
{
cout << "Date中的拷贝构造" << endl;
}
/*Date(const Date&& de)
:_a(de._a)
{
cout << "Date中的移动构造" << endl;
}*/
Date& operator=(const Date& de)
{
_a = de._a;
cout << "Date中的移动赋值" << endl;
return *this;
}
private:
int _a;
};
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:
cl::string _name; //姓名
int _age; //年龄
};
现在,我们对以上结论验证:
当我们屏蔽Person类中的移动构造时,对于内置类型会完成值拷贝,对于自定义类型
类成员变量初始化
由于默认生成的构造函数,会对自定义成员调用其构造函数进行初始化,但并不会对内置成员进行相关处理(一般为随机值),C++11支持非静态成员在声明时进行初始化,默认生成的构造函数会调用缺省值对内置成员初始化.
class Person
{
public:
private:
yzh::string _name;
int _age = 1;
int _Id = 10163;
yzh::A _aa;
};
强制生成默认函数的关键字default
C++1可以让我们更好的控制要使用的默认构造函数,如果当我们要默认使用某个默认的成员函数,但是因为一些原因导致这个函数没有默认生成.
例如: 当我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字指定移动构造生成.
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:
bit::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1;
Person s3 = std::move(s1);
return 0;
}
我们了解,如果当我们显示写了拷贝构造,编译器就不会默认生成移动构造,可是如果我们在移动构造声明处加上关键字default,编译器就可以默认生成了.
禁止生成默认函数的关键字delete
如果想要限制某些默认函数的生成,在C++98中,是将该函数设置成private,并且只声明补丁而已,这样他人调用时就会报错,在C++11中更简单了,只需要在函数声明处加上=delete即可,该语法指示编译器不再生成对应函数的默认版本.
class Person
{
public:
Person(const char* name = "", int age = 0)
:_name(name)
, _age(age)
{}
Person(const Person& p)
:_name(p._name)
, _age(p._age)
{}
//C++11
Person(Person&& p) = delete;
private:
//C++98
//Person(Person&& p)
//{}
yzh::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1; //拷贝构造
Person s3 = std::move(s1); //移动赋值
return 0;
}
继承和多态中的final和override关键字
final修饰基类
如果final修饰基类的话,代表该类无法被继承:
//基类
class Person final //Person类无法被继承,编译错误.
{
public:
virtual void Print()
{
cout << "hello Person" << endl;
}
};
//子类
class Student : public Person
{
public:
virtual void Print()
{
cout << "hello Student" << endl;
}
};
final修饰虚函数
如果final修饰虚函数,代表该虚函数不能在子类中重写:
//基类
class Person
{
public:
virtual void Print() final
{
cout << "hello Person" << endl;
}
};
//子类
class Student : public Person
{
public:
virtual void Print() //重写,编译报错
{
cout << "hello Student" << endl;
}
};
override修饰虚函数
override用于修饰子类的虚函数,主要用于判断子类中是否重写了该虚函数,如果没有重写,则编译报错,所以为了在项目中方便调试,一般在子类的虚函数中加上override.
//基类
class Person
{
public:
virtual void Print()
{
cout << "hello Person" << endl;
}
};
//子类
class Student : public Person
{
public:
virtual void Prints() override
{
cout << "hello Student" << endl;
}
};