五、模板
5.5.3 类中特殊的关键字
1> explicit :该关键字修饰的类,不能进行隐式调用
当类中的构造函数只有一个形参时,可以进行隐士调用该构造函数 使用格式: 类名 对象名 = 形参类型变量;
如果该构造函数前加了该关键字,那么,就不允许上面的隐士调用,只能进行显式调用构造函数
2> delete:该关键字用于类中,可以表示将类中的某些成员函数删除
3> default:该关键字用于类中,可以表示使用该类中自动提供的某些特殊成员函数
4> noexcept:该关键字修饰的函数,表示在使用过程中一定不会抛出异常
#include <iostream>
using namespace std;
class MtClass
{
private:
string name;
int age;
public:
MtClass() = default; //表示使用系统提供的默认的无参构造
explicit MtClass(string n)noexcept:name(n) {} //不能再进行隐式调用了
explicit MtClass(int a):age(a) {}
MtClass(string n, int a):name(n), age(a) {}
MtClass(const MtClass &) = delete; //表示该类,不能在使用拷贝构造函数
~MtClass() {}
void show()
{
cout<<"name = "<<name<<" age = "<<age<<endl;
}
};
int main()
{
MtClass c1(20); //显式调用有参构造函数
MtClass c2("张三"); //显式调用有参构造
MtClass c4; //无参构造
//MtClass c3(c1); //类中删除了拷贝构造函数,所以不能进行该操作
// MtClass c3 = 18; //隐式调用
// MtClass c4 = string("李四"); //隐式调用
return 0;
}
5.5.4 list:链表
1> 常用函数
1、构造函数
list(); //无参构造
list( size_type count,
const T& value,
const Allocator& alloc = Allocator()); //使用count个元素进行初始化一个链表
explicit list( size_type count ); //构造一个链表,有count个随机元素
list( const list& other ); //拷贝构造
list( list&& other ); //移动构造
list( InputIt first, InputIt last, const Allocator& alloc = Allocator() ); //构造拥有范围 [first, last) 内容的容器。
2、back()函数返回一个引用,指向list的最后一个元素。
3、begin()函数返回一个迭代器,指向list的第一个元素
4、clear()函数删除list的所有元素。
5、empty()函数返回真(true)如果链表为空,否则返回假。
6、end()函数返回一个迭代器,指向链表的末尾。
7、iterator erase( iterator pos ); //删除指定位置的元素
iterator erase( iterator start, iterator end ); //删除范围内的元素
8、 front()函数返回一个引用,指向链表的第一个元素
9、 iterator insert( iterator pos, const TYPE &val ); //在指定位置前插入一个元素
void insert( iterator pos, size_type num, const TYPE &val ); //在指定位置前插入num个元素
void insert( iterator pos, input_iterator start, input_iterator end ); //在指定位置前插入给定区间内的元素
10、pop_back()函数删除链表的最后一个元素。
11、 pop_front()函数删除链表的第一个元素。
12、push_back()将val连接到链表的最后
13、push_front()函数将val连接到链表的头部。
14、void remove( const TYPE &val ); //删除链表中所有值为val的元素
15、reverse()函数把list所有元素倒转。
16、 void sort(); //默认从小到大升序排序
void sort( Comp compfunction ); //加策略的排序
17、 unique()函数删除链表中所有重复的元素
2> 链表实现实例
#include <iostream>
#include<list> //链表头文件
using namespace std;
int main()
{
list<int> l1; //定义一个存储整形数据的链表 无参构造
list<int> l2(3,520); //定义一个存储整形数据的链表,初始化为3个 520 有参构造
int arr[5] = {1,2,3,4,5};
list<int> l3(arr, arr+5); //定义一个存储整形数据的链表,初始化为一个数组中的内容
cout<<l3.size()<<endl; //求大小
cout<<l3.max_size()<<endl; //计算机能给链表分配的总大小
cout<<l3.front()<<endl; //访问第一个元素
l3.front() = 666; //可读可写
cout<<l3.back()<<endl; //最后一个元素 可读可写
l3.push_front(520); //头插
l3.push_back(1314); //尾插
for(auto val:l3)
{
cout<<val<<" ";
}
cout<<endl;
l3.pop_back(); //尾删
l3.pop_front(); //头删
l3.insert(l3.begin(), 100); //在第一个位置前添加一个元素
l3.insert(l3.end(), 10000); //在最后一个位置前添加一个元素
for(auto val:l3)
{
cout<<val<<" ";
}
cout<<endl;
l3.remove(666); //将链表中为666的元素删除
l3.reverse(); //将链表翻转
for(auto val:l3)
{
cout<<val<<" ";
}
cout<<endl;
l3.sort(); //将链表排序
for(auto val:l3)
{
cout<<val<<" ";
}
return 0;
}
六、智能指针(重要)
一、引入背景
1> C++中在堆区申请空间和释放空间需要使用new和delete完成
2> 多个指针指向同一个内存空间,释放其中一个的空间,另一个还在继续使用(悬空指针)
3> 只是申请了内存空间,使用后忘记释放内存空间,堆区对象没有得到析构
4> 栈取申请的对象空间,在脱离对象空间后,会自动调用析构函数完成资源的回收,但是堆区的不会自动释放
#include <iostream>
using namespace std;
class Test
{
private:
string name;
public:
Test() {cout<<"Test::无参构造"<<endl;}
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;}
~Test() {cout<<name<<"::析构函数"<<endl;}
};
int main()
{
Test *p = new Test("张飞");
cout << "Hello World!" << endl;
return 0;
}
效果图
5> 总结:上述例子中,在堆区申请一个对象空间,但是没有手动释放空间,造成对象没有析构,为了保证堆区空间使用的安全性,我们引入了智能指针,目的是更加安全的使用堆区空间。
二、智能指针
1> C++11标准提供了两种智能指针,分别是unique_ptr(独占智能指针)、shared_ptr(共享智能指针)
2> 除了上述两种指针外,还有auto_ptr(自动智能指针,已弃用)、weak_ptr(弱智能指针,辅助shared_ptr作用)
3> 以上所有智能指针都在头文件:#include<memory>中
4> 智能指针是类模板,在栈区创建智能指针对象
5> 把普通指针交给智能指针管理
6> 智能指针对象过期时,析构函数会自动释放智能指针管理的指针空间
三、unique_ptr(独占智能指针)
3.1 独占智能指针的概述
独占智能指针会“拥有”它所指向的对象,某一时刻,只能有一个unique_ptr指向给定的对象,当该指针被销毁时,指向的对象也会随之释放。
3.2 类的原型
template <typename T, typename D = default_delete<T>>
class unique_ptr
{
public:
explicit unique_ptr(pointer p) noexcept; // 不可用于转换函数。
~unique_ptr() noexcept;
T& operator*() const; // 重载*操作符。
T* operator->() const noexcept; // 重载->操作符。
unique_ptr(const unique_ptr &) = delete; // 禁用拷贝构造函数
unique_ptr& operator=(const unique_ptr &) = delete; // 禁用赋值函数
unique_ptr(unique_ptr &&) noexcept; // 右值引用。
unique_ptr& operator=(unique_ptr &&) noexcept; // 右值引用
private:
pointer ptr; // 内置的指针。
};
3.3 初始化
方法一:
unique_ptr<AA> p0(new AA("西施"));// 分配内存并初始化。
方法二:
unique_ptr<AA> p0 = make_unique<AA>("西施"); // C++14标准。
方法三:
AA* p = new AA("西施");
unique_ptr<AA> p0(p); // 用已存在的地址初始化。
1> 方法三
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
Test *p1 = new Test("张三"); //在堆区申请空间
unique_ptr<Test> up1(p1); //使用原始指针实例化一个指针指针
cout<<"name = "<<(*p1).name<<endl; //指针找到对象使用
cout<<"name = "<<p1->name<<endl; //原始指针直接使用
cout<<"name = "<<(*up1).name<<endl; //智能指针访问
cout<<"name = "<<up1->name<<endl; //智能指针访问
cout << "********程序到此结束!********" << endl;
return 0;
}
2> 使用方式1:
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
unique_ptr<Test> up1(new Test("张三")); //使用原始指针实例化一个指针指针
cout<<"name = "<<(*up1).name<<endl; //智能指针访问
cout<<"name = "<<up1->name<<endl; //智能指针访问
cout << "********程序到此结束!********" << endl;
return 0;
}
3> 使用方法2只有C++14标准后才能使用,C++11标准不支持
3.4 独占智能指针的注意事项
1> 重载的*和->运算符重载,所以可以跟像使用普通指针一样使用该智能指针
2> 不支持拷贝构造和拷贝赋值函数
3> 不要使用一个原始指针初始化多个unique_ptr
4> get()函数可以返回该智能指针的原始指针
5> 不要使用unique_ptr指向非new申请的空间
6> 有智能指针指向原始指针后,就不要再用原始指针手动释放了
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
Test *p1 = new Test("张三");
unique_ptr<Test> up1(p1); //使用原始指针实例化一个指针指针
unique_ptr<Test> up2(p1); //使用一个原始指针实例化多个智能指针
cout<<"name = "<<(*up1).name<<endl; //智能指针访问
cout<<"name = "<<up1->name<<endl; //智能指针访问
cout << "********程序到此结束!********" << endl;
return 0;
}
7> 智能指针作为函数参数传递时,传递引用,不要传递值,因为智能指针没有拷贝构造函数
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
//void fun(unique_ptr<Test> up) //定义时不会报错,但是使用时会报错
void fun(unique_ptr<Test> &up)
{
}
int main()
{
Test *p1 = new Test("张三");
unique_ptr<Test> up1(p1); //使用原始指针实例化一个指针指针
cout<<"name = "<<(*up1).name<<endl; //智能指针访问
cout<<"name = "<<up1->name<<endl; //智能指针访问
//调用函数传递智能指针的值
fun(up1); //报错,会告诉没有拷贝构造函数
cout << "********程序到此结束!********" << endl;
return 0;
}
8> 智能指针不支持:+、-、++、--
3.5 智能指针的更多技巧
1> 将一个智能指针unique_ptr赋值给另一个指针时,可以赋值右值(移动构造存在),但是不能赋值左值(拷贝构造不存在)
2> 使用nullptr给unique_ptr赋值时,会自动释放对象
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
Test *p1 = new Test("张三");
unique_ptr<Test> up1(p1); //使用原始指针实例化一个指针指针
cout<<"name = "<<(*up1).name<<endl; //智能指针访问
cout<<"name = "<<up1->name<<endl; //智能指针访问
up1 = nullptr; //用空指针给智能指针赋值时,会释放智能指针指向的对象
cout << "********程序到此结束!********" << endl;
return 0;
}
3> 使用release()函数,可以释放unique_ptr的指针,并且将原指针返回
4> std::move函数,可以转移原始指针的控制权,本质上调用的时移动构造函数
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
//函数1需要一个指针,但是不对这个指针负责
void fun1(const Test *p)
{
cout<<"fun1::"<<p->name<<endl;
}
//函数2需要一个指针,并且对这个指针负责
void fun2(Test *p)
{
cout<<"fun2::"<<p->name<<endl;
delete p;
}
//函数3需要一个智能指针,但是不对这个指针负责
void fun3(unique_ptr<Test> &up)
{
cout<<"fun3::"<<up->name<<endl;
}
//函数4需要一个智能指针,并对这个指针负责
void fun4(unique_ptr<Test> up)
{
cout<<"fun3::"<<up->name<<endl;
}
int main()
{
unique_ptr<Test> up1(new Test("张飞")); //在堆区空间实例化一个对象,托管到智能指针
cout<<"函数调用开始"<<endl;
// fun1(up1.get()); //调用功能1函数,不会对指针负责
// fun2(up1.release()); //调用功能2函数,会对指针负责
// fun3(up1); //需要使用智能指针的引用,函数内不会对指针负责
fun4(move(up1)); //使用一个智能指针,函数内对指针负责
cout << "********程序到此结束!********" << endl;
return 0;
}
5> reset()函数释放对象
函数原型:void reset(pointer __p = pointer())
使用方法:
up->reset(); //释放up对象指向的资源
up->reset(nullptr); //释放up对象指向的资源
up->reset(new Test("夏侯")); //释放up对原对象的资源,并指向新对象
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
unique_ptr<Test> up1(new Test("张飞")); //在堆区空间实例化一个对象,托管到智能指针
cout<<"函数调用开始"<<endl;
//up1.reset();
up1.reset(new Test("夏侯")); //释放原来对象的空间,指向新的空间
cout << "********程序到此结束!********" << endl;
return 0;
}
6> swap()函数交换两个unique_ptr的使用权
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
unique_ptr<Test> up1(new Test("张飞")); //在堆区空间实例化一个对象,托管到智能指针
unique_ptr<Test> up2(new Test("夏侯"));
cout<<"函数调用开始"<<endl;
up1.swap(up2); //调用两个指针的使用权
cout<<"up1->name = "<<up1->name<<endl; //夏侯
cout<<"up2->name = "<<up2->name<<endl; //张飞
cout << "********程序到此结束!********" << endl;
return 0;
}
7> unique_ptr也可以有原始指针的特性,当指向一个继承体系内的基类对象时,也有多态的特性,如同原始指针管理基类对象和派生类对象一样
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
virtual void show()
{
cout<<"我是父类"<<endl;
}
};
class Demo:public Test
{
public:
void show() override
{
cout<<"我是子类"<<endl;
}
};
int main()
{
unique_ptr<Test> up1(new Demo); //父类指针指向子类对象
cout<<"函数调用开始"<<endl;
up1->show(); //调用子类中重写的父类的虚函数
cout << "********程序到此结束!********" << endl;
return 0;
}
8> 智能指针也不是绝对安全的,当程序内有exit()退出程序时,全局智能指针会释放空间,但是局部智能指针不会
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
virtual void show()
{
cout<<"我是父类"<<endl;
}
};
int main()
{
unique_ptr<Test> up1(new Test("张飞")); //父类指针指向子类对象
cout<<"函数调用开始"<<endl;
exit(0); //手动退出程序
cout << "********程序到此结束!********" << endl;
return 0;
}
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
virtual void show()
{
cout<<"我是父类"<<endl;
}
};
//全局定义智能指针
unique_ptr<Test> up1(new Test("张飞")); //父类指针指向子类对象
int main()
{
cout<<"函数调用开始"<<endl;
exit(0); //手动退出程序
cout << "********程序到此结束!********" << endl;
return 0;
}
9> unique_ptr提供了支持数组的具体化版本数组版本的unique_ptr,重载了[]运算符,返回的是引用,可以作为左值使用
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
//1、普通指针指向数组的使用
// Test *p = new Test[2];
Test *p = new Test[2]{string("张飞"), string("夏侯")};
// p[0].name = "张飞";
// p[1].name = "夏侯";
// cout<<"p[0].name = "<<p[0].name<<" p[1].name = "<<p[1].name<<endl;
// delete [] p;
//2、使用智能指针指向数组
unique_ptr<Test[]> up(new Test[2]); //类型后需要加一个空的中括号
//unique_ptr<Test[]> up(new Test[2]{string("张飞"), string("夏侯")});
up[0].name = "张飞";
up[1].name = "夏侯";
cout<<"up[0].name = "<<up[0].name<<" up[1].name = "<<up[1].name<<endl;
cout << "********程序到此结束!********" << endl;
return 0;
}
四、shared_ptr(共享智能指针)
4.1 概述
shared_ptr共享他指向的对象,多个共享指针可以指向(关联)相同的对象,在内部采用计数器机制来实现
当新的shared_ptr与对象关联时,引用计数器增加1
当shared_ptr超出作用域时,引用计数器减1
当引用计数器变为0时,则表示没有任何shared_ptr与对象关联,则释放该对象
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
Test *p = new Test("张飞"); //使用原始指针指向一个对象
shared_ptr<Test> sp1(p); //构造一个共享智能指针
cout<<"sp1.use_count = "<<sp1.use_count()<<endl; //显式当前指针的引用计数器 1
cout << "********程序到此结束!********" << endl;
return 0;
}
4.2 基本使用
shared_ptr的构造函数也是explicit,但是没有删除拷贝构造函数和拷贝赋值函数
1> 初始化
方法一:
shared_ptr<AA> p0(new AA("西施"));// 分配内存并初始化。
方法二:
shared_ptr<AA> p0 = make_share<AA>("西施"); // C++11标准,推荐使用,效率更高
方法三:
AA* p = new AA("西施");
shared_ptr<AA> p0(p); // 用已存在的地址初始化。
方法四:
shared_ptr<AA> p0(new AA("西施"));// 分配内存并初始化。
shared_ptr<AA> p1(p0); //拷贝构造
shared_ptr<AA> p2 = p0; //拷贝构造
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
Test *p = new Test("张飞"); //使用原始指针指向一个对象
shared_ptr<Test> sp1(p); //构造一个共享智能指针
cout<<"sp1.use_count = "<<sp1.use_count()<<endl; //显式当前指针的引用计数器 1
shared_ptr<Test> sp2(sp1); //拷贝构造
shared_ptr<Test> sp3 = sp2; //拷贝构造
cout<<"sp1.use_count = "<<sp1.use_count()<<endl; //显式当前指针的引用计数器 3
cout<<"sp2.use_count = "<<sp2.use_count()<<endl; //显式当前指针的引用计数器 3
cout<<"sp3.use_count = "<<sp3.use_count()<<endl; //显式当前指针的引用计数器 3
cout << "********程序到此结束!********" << endl;
return 0;
}
2> 使用方法
1、智能指针重载了*和->操作符,可以像使用原始指针一样使用shared_ptr
2、use_count()函数返回引用计数器的个数
3、unique()函数,如果use_count()为1,返回true,否则返回false
4、支持普通的拷贝和赋值,左值的shared_ptr的计数器减1,右值的shared_ptr的计数器将加1
left = right;
5、get()函数返回原始指针
6、不要用同一个原始指针区初始化多个shared_ptr
7、不要使用shared_ptr管理不是new分配的空间内存
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
shared_ptr<Test> spa1(new Test("张飞")); //构造一个共享智能指针
shared_ptr<Test> spa2(spa1); //拷贝构造
shared_ptr<Test> spa3 = spa2; //拷贝构造
cout<<"spa1.use_count = "<<spa1.use_count()<<endl; //张飞的指针个数 3
shared_ptr<Test> spb1(new Test("夏侯")); //构造夏侯的指针
shared_ptr<Test> spb2(spb1);
cout<<"spb1.use_count = "<<spb1.use_count()<<endl; //夏侯的引用计数器 2
spa2 = spb2; //夏侯的引用计数器将加1,张飞的引用计数器减1
cout<<"spa1.use_count = "<<spa1.use_count()<<endl; //张飞的指针个数 2
cout<<"spb1.use_count = "<<spb1.use_count()<<endl; //夏侯的引用计数器 3
cout << "********程序到此结束!********" << endl;
return 0;
}
3> 用于函数参数
跟unique_ptr一样
4> 不支持指针的运算:+、-、++、--
4.3 更多细节
1> 使用nullptr给shared_ptr赋值时,将计数器减1,如果计数器为0,释放对象资源,空的shared_ptr = nullptr;
2> str::move() 可以转移对原始指针的控制权,还可以将unique_ptr转变成shared_ptr
3> reset()改变与资源的关联关系
pp.reset(); //解除与资源的关系,资源的引用计数器减1
pp.reset(new AA("bbb")); //解除与资源的关系,引用计数器减1,关联新资源
4> swap()函数交换两个shared_ptr的控制权
5> shared_ptr也可以像普通指针那样,完成多态的性质,使用方式跟原始指针一致
6> shared_ptr不是绝对安全的,如果程序中调用了exit()退出,全局的shared_ptr会自动释放,局部的shared_ptr无法释放
7> shared_ptr提供的智能指针数组的具体化版本,重载了[],返回值是一个引用,可以作为左值使用
8> shared_ptr的线程安全性:
shared_ptr的引用计数器是线程安全的(引用计数器是原子操作)
多个线程同时读一个shared_ptr对象是线程安全的
如果多个线程对同一个shared_ptr对象进行读和写,则需要加锁
多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,都要加锁进行保护
9> 实际开发过程中,unique_ptr能解决的问题,尽量不要使用shared_ptr,因为unique_ptr效率更高,所用资源更少
五、智能指针的删除器(了解)
在默认情况下,智能指针过期的时候,用 delete 原始指针;
释放管理的资源程序员也可以自定义删除器,改变智能指针释放资源的行为
删除器可以是全局函数、仿函数以及Lambda表达式,形参为原始指针
#include <iostream>
#include<memory>
using namespace std;
class Test
{
public:
string name; //名字
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
//全局函数作为删除器
void deletefunc(Test *p)
{
cout<<"自定义删除器:全局函数实现"<<endl;
delete p;
}
//定义仿函数作为删除器
class deleteclass
{
public:
void operator()(Test *p)
{
cout<<"仿函数实现删除器"<<endl;
delete p;
}
};
//定义lambda作为删除器
auto deleteLamb = [](Test *p){
cout<<"lambda表达式作为删除器"<<endl;
delete p;
};
int main()
{
//shared_ptr<Test> spa1(new Test("张飞")); //使用默认的删除器
/*shared_ptr<Test> spa1(new Test("张飞"),deletefunc);*/ //使用全局函数作为删除器
// shared_ptr<Test> spa1(new Test("张飞"),deleteclass()); //使用仿函数作为删除器
// shared_ptr<Test> spa1(new Test("张飞"),deleteLamb);
//独占智能指针的删除器的使用
// unique_ptr<Test, decltype (deletefunc)*> up1(new Test("夏侯"), deletefunc); //全局函数作为删除器
unique_ptr<Test, void (*)(Test *)> up1(new Test("夏侯"), deletefunc); //使用函数指针推导
// unique_ptr<Test, deleteclass> up1(new Test("夏侯"), deleteclass()); //仿函数作为删除器
// unique_ptr<Test, decltype (deleteLamb)> up1(new Test("夏侯"), deleteLamb); //lambda表达式作为删除器
cout << "********程序到此结束!********" << endl;
return 0;
}
六、weak_ptr(弱智能指针)
6.1 引入目的
shared_ptr内部维护了一个共享的引用计数器,多个shared_ptr可以指向同一个资源如果出现了循环引用的情况,引用计数器将永远无法归0,资源不会被释放
#include <iostream>
#include<memory>
using namespace std;
class Demo;
class Test
{
public:
string name; //名字
shared_ptr<Demo> sp; //共享智能指针
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
class Demo
{
public:
string name; //名字
shared_ptr<Test> sp; //共享智能指针
Demo() {cout<<name<<"::无参构造"<<endl;} //无参构造
Demo(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Demo() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
shared_ptr<Test> sp1(new Test("张飞"));
shared_ptr<Demo> sp2(new Demo("夏侯"));
//将他们的彼此的共享指针成员指向彼此
sp1->sp = sp2;
sp2->sp = sp1;
cout << "********程序到此结束!********" << endl;
return 0;
}
此时可以使用弱智能指针解决
#include <iostream>
#include<memory>
using namespace std;
class Demo;
class Test
{
public:
string name; //名字
weak_ptr<Demo> sp; //弱智能指针
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
class Demo
{
public:
string name; //名字
weak_ptr<Test> sp; //弱智能指针
Demo() {cout<<name<<"::无参构造"<<endl;} //无参构造
Demo(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Demo() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
int main()
{
shared_ptr<Test> sp1(new Test("张飞"));
shared_ptr<Demo> sp2(new Demo("夏侯"));
//将他们的彼此的共享指针成员指向彼此
sp1->sp = sp2;
sp2->sp = sp1;
cout << "********程序到此结束!********" << endl;
return 0;
}
6.2 weak_ptr是什么
1> weak_ ptr 是为了配合shared_ ptr 而引入的,它指向一个由shared_ ptr 管理的资源但不影响资源的生命周期。也就是说,将-个weak_ ptr 绑定到一个shared_ ptr 不会改变shared_ ptr 的引用计数。
2> 不论是否有weak_ ptr 指向,如果最后一个指向资源的shared_ ptr 被销毁,资源就会被释放。
3> weak_ ptr更像是shared_ ptr 的助手而不是智能指针。
6.3 如何使用weak_ptr
1> weak_ptr没有重载->和*运算符,不能直接访问资源
2> operator=(); 把shared_ptr或weak_ptr赋值给weak_ptr
3> expired(); 判断它指向的资源释放已经过期
4> lock(); 返回shared_ptr,如果资源已经过期,返回空的shared_ptr
5> reset(); 将当前的weak_ptr置空
6> swap(); 交换
7> weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着
用lock()函数可用把它提升为shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的shared_ptr 提升的行为(lock)是线程安全的
6.4 weak_ptr的使用
1> 单线程可用,多线程不安全版本
#include <iostream>
#include<memory>
using namespace std;
class Demo;
class Test
{
public:
string name; //名字
weak_ptr<Demo> sp; //弱智能指针
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
class Demo
{
public:
string name; //名字
weak_ptr<Test> sp; //弱智能指针
Demo() {cout<<name<<"::无参构造"<<endl;} //无参构造
Demo(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Demo() {cout<<name<<"::析构函数"<<endl;
} //析构函数
};
int main()
{
shared_ptr<Test> sp1 = make_shared<Test>("张飞"); //创建一个共享智能指针
{
shared_ptr<Demo> sp2 = make_shared<Demo>("夏侯"); //在局部空间创建一个新对象
//两个类对象的弱指针成员指向对方
sp1->sp = sp2;
sp2->sp = sp1;
if(sp1->sp.expired() == true)
{
cout<<"语句块内部:sp1->sp已经过期"<<endl;
}else
{
cout<<"语句块内部:sp1->sp.lock()->name = "<<sp1->sp.lock()->name<<endl;
}
}
if(sp1->sp.expired() == true)
{
cout<<"语句块外部:sp1->sp已经过期"<<endl;
}else
{
cout<<"语句块外部:sp1->sp.lock()->name = "<<sp1->sp.lock()->name<<endl;
}
cout << "********程序到此结束!********" << endl;
return 0;
}
2> 多线程安全版本
#include <iostream>
#include<memory>
using namespace std;
class Demo;
class Test
{
public:
string name; //名字
weak_ptr<Demo> sp; //弱智能指针
Test() {cout<<name<<"::无参构造"<<endl;} //无参构造
Test(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Test() {cout<<name<<"::析构函数"<<endl;} //析构函数
};
class Demo
{
public:
string name; //名字
weak_ptr<Test> sp; //弱智能指针
Demo() {cout<<name<<"::无参构造"<<endl;} //无参构造
Demo(string n):name(n) {cout<<name<<"::有参构造"<<endl;} //有参构造
~Demo() {cout<<name<<"::析构函数"<<endl;
} //析构函数
};
int main()
{
shared_ptr<Test> sp1 = make_shared<Test>("张飞"); //创建一个共享智能指针
{
shared_ptr<Demo> sp2 = make_shared<Demo>("夏侯"); //在局部空间创建一个新对象
//两个类对象的弱指针成员指向对方
sp1->sp = sp2;
sp2->sp = sp1;
//定义一个共享智能指针,接收提升后的weak_ptr
shared_ptr<Demo> t = sp1->sp.lock(); //原子操作,线程安全
if(t == nullptr)
{
cout<<"语句块内部:sp1->sp已经过期"<<endl;
}else
{
cout<<"语句块内部:t->name = "<<t->name<<endl;
}
}
//定义一个共享智能指针,接收提升后的weak_ptr
shared_ptr<Demo> t = sp1->sp.lock(); //原子操作,线程安全
if(t == nullptr)
{
cout<<"语句块外部:sp1->sp已经过期"<<endl;
}else
{
cout<<"语句块外部:t->name = "<<t->name<<endl;
}
cout << "********程序到此结束!********" << endl;
return 0;
}
七、C++中类型的转换
八、C++中关键字总结
1> C++中一共有63个关键字,如上图所示,其中标红的为c语言中的关键字,有32个
2> 数据类型相关的关键字
bool、true、false:对于bool类型数据的相关处理,值为true和false
char、wchar_t:char是单字符数据,wchar_t多字符数据
int、short、float、double、long:整数和实数的数据类型
signed、unsigned:定义有符号和无符号数据的说明符
auto:在c语言中,是存储类型,但是在C++中,是类型自动推导,注意事项有两个:
i> 连续定义多个变量时,初始值必须是相同数据类型,否则报错
ii> auto p=&m; 与auto* p = &m;规定是一样
explicit:防止数据隐式转换
typedef:类型重定义
sizeof:求数据类型的字节运算
3> 语句相关的关键字
switch、case、default:实现多分支选择结构
do、while、for:循环相关的关键字
break、continue、goto:跳转语句
if、else:选择结构
inline:内联函数
return:函数返回值
4> 存储类型相关的关键字
static、const、volatile、register、extern、auto
5> 构造数据类型相关
struct、union:结构体和共用体
enum:枚举
class:类
6> 访问权限:public、protected、private
7> 异常处理:throw、try、catch
8> 类中相关使用关键字
this:指代自己的指针
friend:友元
virtual:虚
delete、default:对类的特殊成员函数的相关使用
例如:Test(const Test &) = default; ~Test() = delete;
mutable:取消常属性
using:引入数据,有三种使用方式
i> 使用命名空间的关键字
ii> 相当于类型重定义
iii> 修改子类中从父类继承下来成员的权限
operator:运算符重载关键字
9> 类型转换相关的关键字
static_cast、dynamic_cast、const_cast、reinterpret_cast
10> 模板相关的关键字:template、typename
11> 命名空间相关:using、namespace
12> export:导入相关模板类使用
13> 内存分配和回收:new、delete
补充的枚举内容
using namespace std;
//使用enum可以定义枚举,枚举值都是整型常量
//如果没有指定值,默认时从0开始
//枚举的作用:防止魔鬼数字
enum Color
{
red = 10, //10
orange, //11
yellow, //12
green = 100, //100
cyan, //101
blue, //102
puple //103
};
int main()
{
Color c1 = yellow;
cout<<c1<<endl; //12
cout<<blue<<endl; //102
if(c1 == green)
{
cout<<"**********"<<endl;
}else
{
cout<<"--------------"<<endl;
}
return 0;
}