day49——杂项

 五、模板

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值