C++11 – 新功能
默認(rèn)成員函數(shù)
在C++11之前一個類有6個默認(rèn)成員函數(shù),在C++11標(biāo)準(zhǔn)中又新增了兩個默認(rèn)成員函數(shù),分別是移動構(gòu)造函數(shù)和移動賦值函數(shù)
默認(rèn)移動構(gòu)造和移動賦值生成的條件
- 移動構(gòu)造函數(shù)的生成條件:沒有自己實現(xiàn)移動構(gòu)造函數(shù),并且沒有自己實現(xiàn)析構(gòu)函數(shù),拷貝構(gòu)造函數(shù)和拷貝賦值函數(shù)
- 移動賦值重載函數(shù)的生成條件:沒有自己實現(xiàn)移動賦值重載函數(shù),并且沒有自己實現(xiàn)析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),拷貝賦值函數(shù)
也就是說移動構(gòu)造和移動賦值的生成條件和之前六個默認(rèn)成員函數(shù)不同,并不是不寫就會生成默認(rèn)的。
特別注意,如果我們自己實現(xiàn)了移動構(gòu)造和移動賦值,就算沒有實現(xiàn)拷貝構(gòu)造和拷貝賦值,先一起也不會生成默認(rèn)的拷貝構(gòu)造和拷貝賦值
默認(rèn)移動構(gòu)造函數(shù)會做什么
- 默認(rèn)生成的移動構(gòu)造函數(shù):對于內(nèi)置類型的成員可以完成值拷貝(淺拷貝),對于自定義類型的成員,如果該成員實現(xiàn)了移動構(gòu)造就調(diào)用它的移動構(gòu)造,否則就調(diào)用它的拷貝構(gòu)造
- 默認(rèn)生成生成的移動賦值函數(shù):對于內(nèi)置類型的成員會完成值拷貝(淺拷貝),對于自定義類型成員,如果該成員實現(xiàn)了移動賦值就調(diào)用它的移動賦值,否則就調(diào)用它的拷貝賦值
驗證默認(rèn)生成的移動構(gòu)造函數(shù)和移動賦值函數(shù)所做的工作
要驗證默認(rèn)生成的移動構(gòu)造和移動賦值所做的工作,這里使用了類一個簡化版的string類,其中只編寫了幾個我們需要的成員函數(shù)
//
// Created by 陳李鑫 on 2023/7/16.
//
#ifndef SIMULATION_REALIZATION_STL_CLX_STRING_HPP
#define SIMULATION_REALIZATION_STL_CLX_STRING_HPP
#endif //SIMULATION_REALIZATION_STL_CLX_STRING_HPP
#include <iostream>
#include <algorithm>
#include <utility>
#include <cassert>
class clx_string{
public:
typedef char* iterator;
iterator begin() { return _str;}
iterator end() { return _str + _size; }
const char* c_str() const { return const_cast<const char*>(_str); };
void swap(clx_string& s);
clx_string(const char* str = "");
clx_string(const clx_string& s);
clx_string(clx_string&& s);
~clx_string();
clx_string& operator=(const clx_string& s);
clx_string& operator=(clx_string&& s);
char& operator[](size_t i);
void reserve(size_t n);
void push_back(char ch);
clx_string& operator+=(char ch);
static clx_string to_string(int value);
private:
char* _str;
size_t _size;
size_t _capacity;
};
void clx_string::swap(clx_string& s) {
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
std::swap(_str, s._str);
}
clx_string::clx_string(const char* str) {
std::cout << "clx_string(const char* str) -- 直接構(gòu)造" << std::endl;
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
clx_string::clx_string(clx_string&& s)
:_size(0), _capacity(0), _str(nullptr)
{
std::cout << "clx_string::clx_string(clx_string&& s) -- 移動構(gòu)造" << std::endl;
swap(s);
}
// 拷貝構(gòu)造函數(shù) 以前的寫法
//clx_string::clx_string(const clx_string& s) {
// _size = strlen(s.c_str());
// _capacity = _size;
// _str = new char[_capacity + 1];
// strcpy(_str, s.c_str());
//}
// 拷貝構(gòu)造函數(shù) 現(xiàn)代寫法
clx_string::clx_string(const clx_string& s)
: _str(nullptr), _size(0), _capacity(0)
{
std::cout << "clx_string(const clx_string& s) -- 拷貝構(gòu)造" << std::endl;
clx_string tmp(s.c_str());
swap(tmp);
std::cout << std::endl;
std::cout << std::endl;
}
clx_string::~clx_string() {
_size = 0;
_capacity = 0;
delete[] _str;
_str = nullptr;
}
clx_string& clx_string:: operator=(const clx_string& s) {
std::cout << "clx_string& clx_string:: operator=(const clx_string& s) -- 賦值函數(shù)重載" << std::endl;
clx_string tmp(s.c_str());
clx_string::swap(tmp);
std::cout << std::endl;
std::cout << std::endl;
return *this;
}
clx_string& clx_string::operator=(clx_string&& s) {
std::cout << "clx_string& clx_string::operator=(clx_string&& s) -- 移動賦值重載" << std::endl;
swap(s);
return *this;
}
char& clx_string::operator[](size_t i) {
assert(0 <= i && i < _size);
return _str[i];
}
void clx_string::reserve(size_t n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
strncpy(tmp, _str, _size + 1);
if (_str) {
delete[] _str;
}
_str = tmp;
_capacity = n;
}
}
void clx_string::push_back(char ch) {
while (_size >= _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
_str[_size + 1] = 0;
_size++;
}
clx_string& clx_string::operator+=(char ch) {
push_back(ch);
return *this;
}
clx_string clx_string::to_string(int value) {
clx_string res;
bool flag = false;
if (value < 0) {
flag = true;
value = -1 * value;
}
while (value > 0) {
char ch = static_cast<char>(value % 10);
res += ch + '0';
value /= 10;
}
if(flag) res += '-';
std::reverse(res.begin(), res.end());
return res;
}
然后再編譯一個簡單的Person類,Person類中的成員name的類型就是我們模擬實現(xiàn)的string類
class Person{
public:
explicit 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;
}
private:
clx_string _name; // 姓名
int _age; // 年齡
};
雖然Person類當(dāng)中沒有實現(xiàn)移動構(gòu)造和移動賦值,但是拷貝構(gòu)造,拷貝賦值,析構(gòu)函數(shù)都實現(xiàn)了,因此Person類不會生成默認(rèn)的移動構(gòu)造和移動賦值
void clx_person_test1() {
Person s1("clx", 21);
Person s2 = std::move(s1);
}
// 輸出
clx_string(const char* str) -- 直接構(gòu)造
clx_string(const clx_string& s) -- 拷貝構(gòu)造
clx_string(const char* str) -- 直接構(gòu)造
可以看到我們已經(jīng)使用右值取構(gòu)造s2但是Person類并沒有調(diào)用默認(rèn)生成的移動構(gòu)造函數(shù),因為_name作為自定義類型并沒有調(diào)用其的移動構(gòu)造以及移動賦值。這里調(diào)用的是Person的拷貝構(gòu)造函數(shù),其又調(diào)用了clx_string類的拷貝構(gòu)造函數(shù)
生成默認(rèn)移動構(gòu)造和移動賦值
為了讓Person調(diào)用默認(rèn)生成的移動構(gòu)造函數(shù)和移動賦值函數(shù),我們需要將Person類的拷貝構(gòu)造,拷貝賦值,析構(gòu)函數(shù)都注釋掉,再次運行上述代碼
clx_string(const char* str) -- 直接構(gòu)造
clx_string::clx_string(clx_string&& s) -- 移動構(gòu)造
可以看到Person類默認(rèn)生成了移動構(gòu)造函數(shù),其對自己的自定義成員_name調(diào)用了自定義成員的移動構(gòu)造函數(shù)_,我們還可以改一下代碼看一下默認(rèn)移動賦值的效果
void clx_person_test1() {
Person s1("clx", 21);
Person s2;
s2 = s1;
}
clx_string(const char* str) -- 直接構(gòu)造
clx_string(const char* str) -- 直接構(gòu)造
clx_string& clx_string:: operator=(const clx_string& s) -- 賦值函數(shù)重載
clx_string(const char* str) -- 直接構(gòu)造
類成員變量初始化
默認(rèn)生成的構(gòu)造函數(shù),對于其自定義類型的成員會調(diào)用其構(gòu)造函數(shù)進行初始化,但并不會對內(nèi)置類型的成員進行處理。于是對于C++11支持非靜態(tài)成員變量在聲明時初始化賦值,默認(rèn)生成的構(gòu)造函數(shù)會使用這些缺省值對成員進行初始化
struct student{
string s = "clx";
int age = 18;
};
void clx_student_test1() {
student s;
cout << s.s << endl; // clx
cout << s.age << endl; // 18
}
注意這里只是聲明,是給聲明的成員變量一個缺省值。不是定義!不是定義!
強制生成默認(rèn)函數(shù)關(guān)鍵字default
C++11可以讓我們更好的控制要使用的默認(rèn)成員函數(shù),假設(shè)某些情況我們需要使用某個默認(rèn)成員函數(shù),但是因為某些原因?qū)е聼o法生成這個默認(rèn)成員函數(shù),就可以使用default這個關(guān)鍵字強制其生成
struct student{
student(const student& stu)
: s(stu.s), age(stu.age){}
string s = "clx";
int age = 18;
};
void clx_student_test2() {
student s;
}
這樣就不行,編譯就會報錯。因為Person類中編寫了拷貝構(gòu)造函數(shù),導(dǎo)致無法生成默認(rèn)的構(gòu)造函數(shù)。默認(rèn)的構(gòu)造函數(shù)生成條件是沒有編寫任何類型的構(gòu)造函數(shù),包括拷貝構(gòu)造函數(shù)
struct student{
student() = default; // 默認(rèn)生成構(gòu)造函數(shù)
student(const student& stu)
: s(stu.s), age(stu.age){}
string s = "clx";
int age = 18;
};
這樣我們可以使用default關(guān)鍵字強制生成默認(rèn)構(gòu)造函數(shù)。
default不僅能生成默認(rèn)構(gòu)造,所有默認(rèn)成員函數(shù)都可以用default關(guān)鍵字強制生成,包括移動構(gòu)造和移動賦值.
class Person{
public:
explicit 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(){}
Person(Person&&) = default; // 生成默認(rèn)的移動賦值和拷貝函數(shù)
Person& operator=(Person&&) = default;
private:
clx_string _name; // 姓名
int _age; // 年齡
};
void clx_person_test1() {
Person s1("clx", 21);
Person s2 = move(s1);
}
clx_string(const char* str) -- 直接構(gòu)造
clx_string::clx_string(clx_string&& s) -- 移動構(gòu)造
可以看到默認(rèn)的移動賦值函數(shù)是生成了的
禁用生成默認(rèn)函數(shù)的關(guān)鍵字delete
如果我們想要限制某些默認(rèn)函數(shù)生成時,可以通過一下幾種方式
- C++98: 將函數(shù)設(shè)置成私有,并只聲明不實現(xiàn),這樣外部調(diào)用該函數(shù)就會報錯
- C++11:在該函數(shù)的聲明后面加上=delete,表示不讓編譯器生成該函數(shù)的默認(rèn)版本
final and override 關(guān)鍵字
final 修飾的類
final修飾的類被稱為最終類,最終類無法被繼承
class UnInheritable final {}
Final 修飾虛函數(shù)
final修飾的虛函數(shù),表示該虛函數(shù)不能再被重寫,如果字類繼承后重寫了該虛函數(shù)編譯就會報錯
Override 修飾虛函數(shù)文章來源:http://www.zghlxwxcb.cn/news/detail-582632.html
override修飾的字類虛函數(shù),檢查該類是否是由父類繼承下來的并且必須重寫,如果沒有重寫就會報錯文章來源地址http://www.zghlxwxcb.cn/news/detail-582632.html
到了這里,關(guān)于【C++】C++11 -- 新功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!