当字符串中有一些特殊符号,如\n、\t这些,如果想让这些特殊字符按照原始的值输出,就可以使用R"XXX(需要按照原始值输出的字符串)XXX",其中XXX用作开始和结束标记,必须相同才能通过语法检查,XXX这部分也可以不写
示例:
cout << R"666(helloworld\nhelloworldagain)666" << endl;
cout << R"(helloworld\nhelloworldagain)" << endl;
运行结果:
NULL可以表示0的无类型指针,也可以表示0,有时候用起来会有歧义,因此需要一种专门表示空指针的关键字
int* p = (int*)malloc(sizeof(int));
if (p == nullptr) {
cout << "申请空间失败" << endl;
}
const可以修饰常量,也可以修饰变量为只读,为了单独表示一种常量的含义,引入了关键字constexpr,从而在编译器就能进行替换,提高程序执行的效率
在定义的表达式之前用constexpr关键字修饰,这样的表达式就是常量表达式
示例:
constexpr int num = 10;
返回值是常量表达式的函数可以用constexpr修饰,constexpr放在返回值类型之前。
constexpr可以修饰普通函数、修饰类成员函数、修饰模板函数、修饰构造函数
#include
#include
//常量表达式constexpr
using namespace std;
int main(){
constexpr int res = constfunc1();
return 0;
}
constexpr int constfunc1(){
return 1;
}
报错信息:
修饰模板函数时,如果实际返回值类型不是常量表达式,则constexpr会被忽略,相当于是一个普通的模板函数
构造函数的函数体必须为空,而初始化任务在初始化列表中进行
当不关心数据类型,而重点关注用已经初始化的变量定义一个新的变量时,就可以使用auto关键字进行自动类型推导
示例:
int num = 66;
auto num2 = num;
根据表达式类型推导出类型,表达式可长可短,最短的表达式是一个变量或者数值
示例:
decltype(1 + 3.4) d1 = 666; //double
decltype('a') ch = 'A'; //char
decltype(2) num = 1024; //int
当函数的返回值的类型不确定,需要经过一系列运算才能得到时们就可以先不写具体的返回值类型,用auto关键字替代,在参数列表后面使用箭头(->)加上decltype(表达式),这样程序就能正常运行
示例:
#include
using namespace std;
//返回值类型后置
template<class T1, class T2>
//decltype表达式只要可以正确得到实际的返回值类型即可
//不需要和函数体内的表达式完全一致
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
return t1 + t2;
}
int main(){
cout << add(1, 3.14) << endl;
return 0;
}
final关键字可以防止虚函数被重写,防止类被继承
如果一个虚函数不能被子类重写,那就可以在函数的参数列表后面加上final关键字
示例:
#include
using namespace std;
class Base {
public:
virtual void func1() final{
cout << "base func1()" << endl;
}
};
class Derive : Base {
public:
virtual void func1() {
cout << "derive func1()" << endl;
}
};
int main() {
return 0;
}
报错信息:
如果不想让一个类被继承,就可以给类名后面加上final,当这个类被其它类继承时,就会出错
示例:
#include
using namespace std;
class Base final{
public:
virtual void func1(){
cout << "base func1()" << endl;
}
};
class Derive : Base {
public:
virtual void func1() {
cout << "derive func1()" << endl;
}
};
int main() {
return 0;
}
报错信息:
如果在子类中要求必须重写父类的虚函数,就可以在子类的虚函数参数列表后面使用override关键字进行检查,如果重写了父类的虚函数则编译不会报错,如果没有重写则编译报错
示例:
#include
using namespace std;
class Base{
public:
virtual void func1(){
cout << "base func1()" << endl;
}
};
class Derive : public Base {
public:
//正确重写
virtual void func1() override{
cout << "derive func1()" << endl;
}
//没有重写
virtual void func1lll() override {
cout << "derive func1()" << endl;
}
};
int main() {
return 0;
}
报错信息:
c++11之前模板中出现连续的右尖括号就会被当作右移符号,需要在两个尖括号之间用空格隔开,cpp11中可以支持连续的右尖括号
cpp11允许在函数模板中指定默认的数据类型,也允许在类模板中指定默认的数据类型
示例:
//函数模板的默认参数
template<class T1 = double , class T2 = char>
void show(T1 t1 = 10, T2 t2 = 'x') {
cout << t1 << " " << t2 << endl;
}
void test() {
show(2, 'A'); //2 A
show(); //10 x
show<int>('a', 'a'); //97 a
show<char>('a', 'a'); //a a
show<int, int>('a', 'a'); //97 97
show<char, int>('a', 'a'); //a 97
}
//类模板的默认类型,类型必须从右往左连续
template<class T1, class T2 = char>
class Base1 {
private:
T1 _t1;
T2 _t2;
};
template<class T1 = long, class T2 = char>
class Base2 {
private:
T1 _t1;
T2 _t2;
};
函数的默认模板参数和根据参数类型自动推导同时进行从高到低的优先级:
如果有指定的数据类型,则使用指定的数据类型;
如果可以推导出,则使用推导出的类型;
函数模板推导不出实际类型,则使用默认模板参数;
无法推导出模板参数类型并且也没有默认模板参数,编译器就会报错
cpp11之前typedef可以定义别名,但是对于比较复杂的类型而言不够直观,可读性不好,于是cpp11中的using有了定义别名的功能。
示例1:
using DataType = int; //定义int类型的别名
using func = int(*)(int, int); //定义函数指针
示例2:
//方式1
template<class T>
struct MyMap{
typedef map<int, T> DataType;
};
//方式2
template<class T>
using MyMap2 = map<int, T>;
void test() {
//和下一行等价: MyMap::DataType d1;
MyMap2<string> d1;
d1.insert(make_pair(1, "efwfw"));
d1.insert(make_pair(2, "efwfw"));
}
将初始化数据成员的一部分工作交给其它构造函数,这就是委托构造函数。
示例:
#include
#include
#include
#include
using namespace std;
//template
//刚才出错的原因是不小心,没有将class上一行的模板声明删除,所以
class Test{
private:
int _a;
int _b;
int _c;
public:
Test() {}
Test(int a) :_a(a) {}
Test(int a, int b) :Test(a){
_b = b;
}
Test(int a, int b, int c) :Test(a, b) {
_c = c;
}
void showData() {
cout << _a << " " << _b << " " << _c << endl;
}
};
int main() {
Test test1;
Test test2(10);
Test test3(10, 24);
Test test4(10, 24, 66);
test1.showData();
test2.showData();
test3.showData();
test4.showData();
return 0;
}
运行结果:
当子类中需要使用父类中的变量,并且子类中没有其它数据成员,这个时候就不需要逐一写出子类中的构造函数,直接继承父类中的所有构造函数,语法如下:
using 父类类名::父类类名;
示例:
#include
#include
#include
#include
using namespace std;
class Base {
public:
int _num1 = 0;
int _num2 = 0;
Base() {}
Base(int num1, int num2) :_num1(num1) ,_num2(num2){}
int getSum() {
return _num1 + _num2;
}
};
class Derive : public Base{
public:
//继承父类所有的构造函数
using Base::Base;
};
int main() {
Derive d(1, 2);
Derive d2;
cout << d.getSum() << endl;
cout << d2.getSum() << endl;
return 0;
}
运行结果:
cpp11中允许使用初始化列表初始化任何数据类型,通过在{}内指定数据来进行初始化工作
示例:
#include
using namespace std;
class Person {
private:
string _name;
int _age;
public:
//默认构造
Person(string name = "somebody", int age = 0) :_name(name) ,_age(age) {}
};
int main() {
int tmp = 1;
double d = 33.33;
float f = 1.2;
int arr[] = { 1, 2, 3, 4 };
Person p("zhangsan", 11);
//初始化列表
int tmp2 = { 1 };
double d2{ 33.33 };
float f2 = { 1.2 };
int arr2[]{ 1, 2, 3, 4 };
Person p2 = { "lisi", 22 };
Person p3{ "wangwu", 66 };
return 0;
}
聚合体可以直接使用初始化列表进行初始化
聚合体范围之外的对象在使用初始化列表时,必须有对应的构造函数存在。
cpp11中提供了一种可以存储多个同类型数据的模板类initializer_list,相比vector而言,initializer_list可以在创建的时候接收任意个数的数据,常作为函数的参数使用。
示例:
#include
#include
using namespace std;
int main(){
initializer_list<int> l{1, 2 ,3 ,4, 5};
for(auto* it = l.begin(); it != l.end(); ++it){
cout << *it << " ";
}
cout << endl;
return 0;
}
对于范围确定的变量,cpp11提出了一种简化for循环的形式,对于自定义类型如果要使用范围for必须也提供begin和end方法
for(变量定义 : 变量的定义域){
循环体
}
变量的定义域可以是数组、容器、初始化列表、表达式
示例:
#include
#include
#include
using namespace std;
int main(){
initializer_list<string> l{"I", "am", "a", "good", "man"};
for(auto e : l){
cout << e << " ";
}
cout << endl;
return 0;
}
可调用对象泛指c++中可以使用函数方式调用的对象,当然这种说法对于大多数的可调用对象是适用的,也有一些不适用,对于下文所说的第四种可调用对象这种说法就不适用
可调用对象根据类型的不同有以下四种
示例1:
#include
#include
using namespace std;
//第一种可调用对象:函数指针
void printInfo(string name, int age) {
cout << "name:" << name << "\tage:" << age << endl;
}
int main() {
cout << "第一种可调用对象:函数指针" << endl;
printInfo("cpp11", 12);
return 0;
}
运行结果:
示例2:
#include
#include
using namespace std;
//第二种可调用对象:仿函数
class ObjectTest {
public:
void operator()(string name) {
cout << "My name is " << name << endl;
}
};
int main() {
cout << "第二种可调用对象:仿函数" << endl;
ObjectTest test;
test("zhangsan");
return 0;
}
运行结果:
示例3:
#include
#include
using namespace std;
using funcPtr1 = void(*)(double, double);
//第三种可调用对象:可以被转换为函数指针的类对象
class Caculator{
public:
static void minus(double num1, double num2) {
cout << num1 << " - " << num2 << " = " << num1 - num2 << endl;
}
operator funcPtr1() {
return minus;
}
};
int main() {
cout << "第三种可调用对象:可以被转换为函数指针的类对象" << endl;
Caculator cal;
cal(1, 3);
return 0;
}
运行结果:
示例4:
#include
#include
using namespace std;
//第四种可调用对象:类成员函数指针或者类成员指针
class Year{
public:
int _year = 2022;
void printInfo() {
cout << "今年是" << _year << "年" << endl;
}
};
int main() {
cout << "第四种可调用对象:类成员函数指针或者类成员指针" << endl;
//(1)类成员函数指针
using printMessage = void(Year::*)(void); //定义函数指针
printMessage p1 = &Year::printInfo; //定义对象
Year year;
//必须使用()将year.*p1括起来,因为p1后面的()优先级高于*
(year.*p1)();
//(2)类成员指针
using Ptr = int(Year::*); //定义Year作用域下的int指针
Ptr p2 = &Year::_year;
year.*p2 = -2022;
year.printInfo();
return 0;
}
运行结果:
function作为一种类模板,可以对除了类成员函数指针之外的所有可调用对象进行包装,实现统一方式的调用。
function<返回值类型(参数类型)> 变量名 = 可调用对象;
示例1:
#include
#include
#include
using namespace std;
void print1(string name) {
cout << "print1, name is " << name << endl;
}
class Test {
public:
static void print2(string name) {
cout << "print2, name is " << name << endl;
}
};
class Test2 {
public:
void operator()(string name) {
cout << "print3, name is " << name << endl;
}
};
//定义函数指针
using funcPtr = void(*)(string);
class Test3 {
public:
operator funcPtr() {
return print4;
}
static void print4(string name) {
cout << "print4, name is " << name << endl;
}
};
//可调用对象包装器
int main() {
//1.可调用对象包装器包装普通函数
function<void(string)> f1 = print1;
f1("张三");
//2.可调用对象包装器包装类的静态函数
function<void(string)> f2 = Test::print2;
f2("李四");
//3.可调用对象包装类包装仿函数
Test2 test2;
function<void(string)> f3 = test2;
f3("王麻子");
//4.可调用对象包装类包装可转换为函数指针的类
Test3 test3;
function<void(string)> f4 = test3;
f4("乔峰");
return 0;
}
运行结果:
绑定器bind可以将可调用对象绑定到一起,作为一个仿函数输出,可以通过funcion接收绑定后的结果
bind的两种形式如下:
//形式1:绑定非类成员函数/变量
bind(可调用对象地址,参数或占位符);
//形式2:绑定类成员函数/变量
bind(可调用对象地址,类实例对象地址, 参数或占位符);
bind在绑定参数时,如果参数只有在实际传参的时候才能确定,就可以使用placeholder进行占位,第一个参数就是placeholders::_1,第一个参数就是placeholders::_2,第N个参数就是placeholders::_N(N为自然数)
#include
#include
#include
using namespace std;
string mystrAdd(string str1, string str2) {
return str1 + str2;
}
//绑定非类成员函数
int main() {
//1.绑定时传入全部参数
auto func1 = bind(mystrAdd, "hello ", "world");
//即使调用时传入参数,但发挥作用的还是绑定时的参数
string res = func1("FFF", "lll");
cout << res << endl;
//2.绑定时传入部分参数
auto func2 = bind(mystrAdd, "hello ", placeholders::_1);
res = func2("china");
cout << res << endl;
//3.绑定时不传入参数,使用占位符
auto func3 = bind(mystrAdd, placeholders::_1, placeholders::_2);
res = func3("good", " afternoon");
cout << res << endl;
return 0;
}
运行结果:
#include
#include
#include
using namespace std;
int globalVar = 10;
//绑定类成员函数
class TestClass1 {
public:
//求a的b次方
int mypow(int a, int b) {
int ret = 1;
for (int i = 0; i < b; ++i) {
ret *= a;
}
return ret;
}
};
int main() {
TestClass1 c1;
//1.绑定时传入全部参数
auto f1 = bind(&TestClass1::mypow, &c1, 2, 10);
cout << "2的10次方为: " << f1(3, 2) << endl;
//2.绑定时传入部分参数
auto f2 = bind(&TestClass1::mypow, &c1, 2, placeholders::_1);
cout << "2的6次方为: " << f2(6) << endl;
//3.绑定时不传入参数,全部使用占位符
auto f3 = bind(&TestClass1::mypow, &c1, placeholders::_2, placeholders::_1);
cout << "5的3次方为: " << f3(3, 5) << endl;
return 0;
}
运行结果:
示例2(绑定类成员变量):
#include
#include
#include
using namespace std;
int globalVar = 10;
//绑定类成员变量
class TestClass2{
public:
string key = "gwjpgjwjgowjgwojgobbb";
};
int main() {
TestClass2 c;
//设置返回值为引用类型就可以对key进行修改
function<string&(void)> f = bind(&TestClass2::key, &c);
cout << f() << endl;
//修改key
f() = "67890vsvns09inbgtyuj";
cout << f() << endl;
return 0;
}
运行结果:
在c++11之前,每次需要调用一个函数之前就需要提前定义,为了随时进行函数的定义和使用,cpp11引入了一种lambda表达式,它可以方便地进行业务逻辑的定义和调用,并且也可以捕获父作用域的变量。
这种表达式本质上被当作匿名仿函数使用
//定义
auto func = [捕获列表](参数列表)->返回值类型 mutable{
函数体
};
//调用
func(参数);
#include
#include
#include
using namespace std;
int main() {
int a = 10;
int b = 29;
int c = 30;
auto f = [&a, b](){
a += b;
};
f();
cout << "a = " << a << endl;
return 0;
}
运行结果:
#include
#include
#include
using namespace std;
int main() {
auto arr = [](string str1, string str2) ->vector<string>{
return { str1, str2 };
}("blue ", "air");
for (auto e : arr) {
cout << e << endl;
}
return 0;
}
运行结果:
在c++中,能够取地址的被称为左值,不能取地址的被称为右值,例如12就是右值,不能取地址,而一个变量num则是左值,num可以取到他的地址。
c++11中右值分为两种,一种是字面量,如1、2.3这些数值,"helloworld"这类字符串,另一种是将亡值(生命周期即将结束的变量)。
右值引用是一种只能引用右值的引用类型,它是一个右值的别名,通常在变量类型后面加上&&表示正是在定义一个右值引用类型。
示例如下:
int num = 10;
int& r1 = num; //左值引用
int&& r2 = 115; //右值引用
const int& ra = num; //常量的左值引用可以引用同类型的左值
ra = 111; //error,常量的左值引用的值不能发生变化
const int&& rb = 1; //常量的右值引用只能引用同类型的右值
const int& rc = 1; //常量的左值引用可以引用同类型的右值
const int& rd = ra; //常量的左值引用可以引用同类型的左值引用
const int& re = rb; //常量的左值引用可以引用同类型的右值引用
为中间变量取别名示例:
int&& tmp = 1 + 2;
在泛型编程中,当使用T&& 或者auto&& 来推导一个变量的类型时,除了const T&& 和const auto&& 标识一个右值引用,其它都遵循下面两条规则:
示例:
template<class T>
class Test{
public:
void func() {
auto&& ra = 1; //右值引用
int num = 1; //左值
int& rb = num; //左值引用
const int& rc = num; //常量的左值引用
const int&& rd = 1; //常量的右值引用
//非右值使用auto&&推导出的都是左值引用
auto&& r1 = ra; //使用右值引用进行推导
auto&& r2 = rd; //使用常量的右值引用进行推导
auto&& r3 = num; //使用左值进行推导
auto&& r4 = rb; //使用左值引用进行推导
auto&& r5 = rc; //使用常量的左值引用进行推导
//使用const auto&& 推导出的一定是右值引用
const auto&& r11 = 1; //使用右值进行推导
const auto&& r12 = ra; //使用右值引用
const auto&& r13 = rd; //使用常量的右值引用进行推导
const auto&& r14 = num; //使用左值进行推导
const auto&& r15 = rb; //使用左值引用进行推导
const auto&& r16 = rc; //使用常量的左值引用进行推导
}
};
右值引用在作为函数参数进行传递的时候就会被句柄化,编译器就会把这个右值引用视为一个左值引用
示例:
#include
using namespace std;
void printValue(int& val) {
cout << "val是左值引用, 引用的值是 " << val;
}
void printValue(int&& val) {
cout << "val是右值引用, 引用的值是 " << val;
}
void forward(int&& ra) {
//右值引用被句柄化,成为了一个左值引用
printValue(ra);
}
int main() {
forward(1); //右值会被ra接收,成为一个右值引用
return 0;
}
运行结果:
一种参数类型为当前类型右值引用的构造函数,移动的意思指的是将右值引用对象引用的对象内的资源移动到当前构造的对象中。在c++11中如果使用一个右值来构造对象,有移动构造函数优先使用移动构造函数,没有移动构造则调用拷贝构造函数。
移动构造示例:
#include s
using namespace std;
class Counter {
public:
int* _counter;
Counter(int num = 0) :_counter(new int(num)){
cout << "构造函数,新资源的地址: " << _counter << endl;
}
Counter(const Counter& counter) :_counter(new int(*(counter._counter))) {
cout << "拷贝构造函数, 新资源的地址: " << _counter << endl;
}
Counter(Counter&& counter) :_counter(counter._counter) {
cout << "移动构造函数, 资源发生了转移,当前资源的地址是: " << _counter << endl;
counter._counter = nullptr;
}
~Counter() {
cout << "析构函数" << endl;
if (_counter != nullptr) {
cout << _counter << "资源释放了一次" << endl;
delete _counter;
_counter = nullptr;
}
}
};
Counter getInstance() {
Counter c(1);
return c;
}
int main() {
Counter c1 = getInstance();
return 0;
}
运行结果:
move函数的作用是将左值转换为右值
当一个左值不再使用,而需要新建一个同类型的左值时,就可以使用move将不用的左值转换为右值,调用移动构造,实现资源的转移。
示例:
#include
#include //move
using namespace std;
class Test {
public:
Test() {
cout << "构造函数" << endl;
}
~Test() {
cout << "析构函数" << endl;
}
Test(const Test& test) {
cout << "拷贝构造" << endl;
}
//这是个不太规范的移动构造,没有移动任何资源,只是作为move的演示使用
Test(Test&& test) {
cout << "移动构造" << endl;
}
};
int main() {
Test t;
Test t2 = move(t); //把左值t转换为右值
Test t3 = t;
return 0;
}
运行结果:
forward函数可以根据模板参数类型进行转换,传入的参数类型为左值引用则返回参数的左值引用,模板参数类型不是左值引用则返回参数的右值引用形式,因为返回的类型和实际传入的类型相同,不受到右值引用传递的影响,所以叫做完美转发。
forward函数的原型:
//模板参数类型是左值引用则返回参数,而不修改类型
template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
//模板参数类型是非左值,则返回参数的右值引用
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;
示例:
#include
#include //forward
using namespace std;
template<class T>
void printValue(T&& val) {
cout << "参数类型为右值引用, val : " << val << endl;
}
template<class T>
void printValue(T& val) {
cout << "参数类型为左值引用, val : " << val << endl;
}
template<class T>
void fun1(T&& val) {
cout << "不进行完美转发如下: " << endl;
printValue(val); //右值引用被传递为左值
cout << "使用完美转发如下:" << endl;
printValue(forward<T>(val));
}
int main() {
fun1<int>(1); //右值作为参数会被转换为右值引用
return 0;
}
运行结果:
智能指针是一种通过对象生命周期来进行资源管理的模板类,在构造智能指针对象时传入要管理的资源,智能指针对象会在析构时自动释放资源,从而可以帮助我们进行资源管理,防止内存泄漏。同时智能指针对象可以通过->运算符调用管理的资源,拥有指针的特性。
比较实用的智能指针有三种shared_ptr,weak_ptr,unique_ptr
shared_ptr是一种通过引用计数方式来进行资源管理的智能指针模板类,多个shared_ptr对象共同管理一份资源会使用计数器进行计数,当有shared_ptr对象和资源断开联系时会计数减1,当有新的智能指针对象和其它shared_ptr对象共同管理一份资源时会计数加1。
经常使用的方法如下:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
using delPtr = void(*)(Person p[]);
int main() {
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person); //构造函数
shared_ptr<Person> ptr2(ptr1); //拷贝构造
shared_ptr<Person> ptr3(move(ptr1)); //移动构造
//定制删除器
shared_ptr<Person[]> ptr4(new Person[2], [](Person p[]) {
delete[] p;
});
shared_ptr<Person> ptr5 = make_shared<Person>("李四");
return 0;
}
运行结果:
返回和当前shared_ptr对象共同管理同一份资源的share_ptr对象的个数
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
int main() {
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person); //构造函数
cout << "ptr1.usecount : " << ptr1.use_count() << endl;
shared_ptr<Person> ptr2(ptr1); //拷贝构造
cout << "ptr1.usecount : " << ptr1.use_count() << endl;
cout << "ptr2.usecount : " << ptr2.use_count() << endl;
return 0;
}
运行结果:
让shared_ptr对象和当前管理的资源断开联系,有新资源的话建立和新资源的联系
两种形式如下:
shared_ptr对象.reset(); //断开和管理的资源的联系
shared_ptr对象.reset(新资源指针); //断开和之前管理的资源的联系,并管理新资源
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
int main() {
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person); //构造函数
ptr1.reset();
shared_ptr<Person> ptr2(new Person("李四"));
ptr2.reset(new Person("王麻子"));
return 0;
}
运行结果:
有两种方式可以获取到管理的资源,一种直接使用->运算符,另一种是使用成员函数get
示例如下:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
void sayName() {
cout << "my name is " << name_ << endl;
}
};
int main() {
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person);
//通过指针特性调用管理的资源
ptr1->sayName();
//通过get方法获取管理的资源
Person* p = ptr1.get();
p->sayName();
return 0;
}
运行结果:
weak_ptr是为了解决shared_ptr循环引用而产生的一种智能指针,当shared_ptr管理的资源中也存在shared_ptr成员时,如果两个shared_ptr对象管理的资源中的shared_ptr对象相互指向就会出现资源无法释放的问题,使用weak_ptr就可以解决循环引用问题,weak_ptr对象可以监控shared_ptr对象,且不会增加引用计数
weak_ptr() //构造一个空对象
weak_ptr(shared_ptr对象) //使用shared_ptr对象构造一个weak_ptr对象
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
int main() {
weak_ptr<Person> ptr; //构造一个空对象
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person);
weak_ptr<Person> ptr2(ptr1);
return 0;
}
运行结果:
返回共同管理同一份资源的shared_ptr对象个数
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
int main() {
weak_ptr<Person> ptr; //构造一个空对象
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person);
weak_ptr<Person> ptr2(ptr1);
cout << "ptr2.use_count:" << ptr2.use_count() << endl;
shared_ptr<Person> ptr3 = ptr1;
cout << "ptr2.use_count:" << ptr2.use_count() << endl;
return 0;
}
运行结果:
和监控的shared_ptr对象断开联系
weak_ptr对象.reset()
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
int main() {
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person);
weak_ptr<Person> ptr2(ptr1);
ptr2.reset();
return 0;
}
判断weak_ptr对象是否为空,空则返回true,否则返回false
weak_ptr对象.expired()
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
int main() {
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person);
weak_ptr<Person> ptr2(ptr1);
if (ptr2.expired()) {
cout << "ptr is empty" << endl;
}
else {
cout << "ptr is not empty" << endl;
}
ptr2.reset();
if (ptr2.expired()) {
cout << "ptr is empty" << endl;
}
else {
cout << "ptr is not empty" << endl;
}
return 0;
}
运行结果:
返回weak_ptr对象监控的shared_ptr对象
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
void sayName() {
cout << "my name is " << name_ << endl;
}
};
int main() {
Person* person = new Person("张三");
shared_ptr<Person> ptr1(person);
weak_ptr<Person> ptr2(ptr1);
shared_ptr<Person> ptr3 = ptr2.lock();
ptr3->sayName();
return 0;
}
运行结果:
不使用weak_ptr的情况:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
shared_ptr<Person> sptr_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
void sayName() {
cout << "my name is " << name_ << endl;
}
};
int main() {
shared_ptr<Person> ptr1(new Person("张三"));
shared_ptr<Person> ptr2(new Person("李四"));
ptr1->sptr_ = ptr2;
ptr2->sptr_ = ptr1;
return 0;
}
运行结果:
使用weak_ptr的情况:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
weak_ptr<Person> sptr_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
void sayName() {
cout << "my name is " << name_ << endl;
}
};
int main() {
shared_ptr<Person> ptr1(new Person("张三"));
shared_ptr<Person> ptr2(new Person("李四"));
ptr1->sptr_ = ptr2;
ptr2->sptr_ = ptr1;
return 0;
}
运行结果:
一种不能使用拷贝构造和赋值运算符重载函数的指针
unique_ptr<资源类型>()
unique_ptr<资源类型>(管理的资源指针)
unique_ptr<资源类型, 删除器类型>(管理的资源指针, 删除器)
unique_ptr<资源类型>(生命周期即将结果的unique_ptr对象)
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
};
using delPtr = void(*)(Person p[]);
int main() {
unique_ptr<Person> ptr; //构造一个空对象
unique_ptr<Person> ptr2(new Person);
cout << "------------------------------------" << endl;
unique_ptr<Person, delPtr> ptr3(new Person[3], [](Person p[]) {
delete[] p;
});
unique_ptr<Person> ptr4(move(ptr2)); //移动构造
return 0;
}
运行结果:
获取unique_ptr管理的资源指针
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
void sayName() {
cout << "我的名字是" << name_ << endl;
}
};
using delPtr = void(*)(Person p[]);
int main() {
unique_ptr<Person> ptr(new Person("愿望"));
//通过指针特性调用
ptr->sayName();
//通过get获取资源指针调用
Person* p = ptr.get();
p->sayName();
return 0;
}
运行结果:
断开和管理资源的联系,如果有新资源指针则管理新资源
示例:
#include
#include
#include
#include
using namespace std;
class Person {
public:
string name_;
Person(string name = "xxx")
:name_(name) {
cout << "Person构造函数" << endl;
}
~Person() {
cout << "Person析构函数" << endl;
}
void sayName() {
cout << "我的名字是" << name_ << endl;
}
};
using delPtr = void(*)(Person p[]);
int main() {
unique_ptr<Person> ptr(new Person("秋天"));
ptr.reset();
cout << "--------------------------------" << endl;
unique_ptr<Person> ptr2(new Person("冬天"));
ptr2.reset(new Person("春天"));
ptr2->sayName();
return 0;
}
运行结果: