C++补充篇- C++11 及其它特性

news2024/9/27 23:34:32

目录

explicit 关键字

 左值和右值的概念

 函数返回值当引用

C++11 新增容器 - array

 C++的类型转换

 static_cast

 reinterpret_cast

 dynamic_cast

 const_cast

 C++智能指针

 auto_ptr 使用详解 (C++98)

 unique_ptr 使用详解 (C++11)

 auto_ptr的弊端

unique_ptr严谨auto_ptr的弊端

unique_ptr 特性 

shared_ptr 使用详解 (C++11)

weak_ptr 使用详解 (自从 C++11)

 shared_ptr的陷阱问题(没析构)

explicit 关键字

作用是表明该构造函数是显示的 , 而非隐式的 . 不能进行隐式转换 ! 跟它相对应的另一个关
键字是 implicit, 意思是隐藏的 , 类构造函数默认情况下即声明为 implicit( 隐式 ).
//student xiaoHua(19, "小花"); //显示构造
//student xiaoMei = { 18, "小美" }; //隐式构造 初始化参数列表,
C++11 前编译不能通过,C++11 新增特性

 左值和右值的概念

按字面意思,通俗地说。以赋值符号 = 为界, = 左边的就是左值( lvalue ), = 右边就是
右值 (rvalue)
int c =    a    +    b;
左值    右值     右值
lvalue - 代表一个在内存中 占有确定位置的对象 (换句话说就是有一个地址)。
rvalue - 通过排他性来定义,每个表达式不是 lvalue 就是 rvalue 。因此从上面的 lvalue 的定义,
rvalue 是在不在内存中占有确定位置的表达式,而是存在CPU的寄存器中( 先转为栈变量然后放进寄存器 )。
所有的 左值 (无论是数组,函数或不完全类型)都可以转换成 右值

 函数返回值当引用

1. 当函数返回值为引用时
        若返回栈变量( 形参和局部变量 int &demo3(int var),int &demo(int **addr) ),不能成为其它 引用(变量是可以的) 的初始值,不能作为左值使用
        int &i1 = demo(&addr);//这个就不行
        int ret = demo(&addr);//这个是可以的
        因为栈变量的值生命周期仅在函数体内部,函数体执行完后会释放
2. 若返回静态变量或全局变量
        可以成为其他引用的初始值 即可作为右值使用,也可作为左值使用
3. 返回形参当引用( int &demo4(int &var) )
        (注: C++ 链式编程中,经常用到引用,运算符重载专题 )

C++11 新增容器 - array

 

array 容器概念
        array 容器是 C++ 11 标准中新增的序列容器,简单地理解,它就是在 C++ 普通数组
的基础上,添加了一些成员函数和全局函数。
        array 是将元素置于一个固定数组中加以管理的容器。
        array 可以随机存取元素 , 支持索引值直接存取, 用 [] 操作符或 at() 方法对元素进行操作, 也可以使用迭代器访问 ,不支持动态的新增删除操作
        array 可以完全替代 C 语言中的数组,使操作数组元素更加安全!
        #include <array>
array 特点
        array 容器的大小是固定的,无法动态的扩展或收缩,这也就意味着,在使用该容器的
过程无法增加或移除元素而改变其大小,它只允许访问或者替换存储的元素。
        STL 还提供有可动态扩展或收缩存储空间的 vector 容器

 array 对象的构造

        array 采用模板类实现, array 对象的默认构造形式
        array<T, int> arrayT; //T 为存储的类型 , 为数值型模板参数,int为个数,长度

 array 的赋值

        a1.assign(0); // 玩法一 改变 array 中所有元素(注:将被废弃,不推荐使用 )
        a1.fill(666); / /玩法二 用特定值填充 array 中所有元素
        array<int, 4> test={1, 2, 3, 4};// 玩法三 定义时使用初始化列表
        array<int, 4> test;
        test={1,2,3,4}; // 玩法四 定义后使用列表重新赋值
        array<int, 4> a1,a2;
        a1={1,2,3,4};
        a2 = a1;// 玩法五 ,赋值运算
        a1.swap(a2); //玩法六 和其它 array 进行交换

 array 的大小

        array.size(); //返回容器中元素的个数

        array.empty(); //判断容器是否为空,逗你玩的,永远为 false
        array.max_size(); //返回容器中最大元素的个数,同 size()。
array 的数据存取
        第一 :使用下标操作 a1[0] = 100;
        第二 :使用 at 方法 如: a1.at(2) = 100;
        第三 :接口返回的引用 a1.front() 和 a1.back()
        注意: 第一和第二种方式必须注意越界

 array 迭代器访问

        array.begin(); //返回容器中第一个数据的迭代器。
        array.end(); //返回容器中最后一个数据之后的迭代器。
        array.rbegin(); //返回容器中倒数第一个元素的迭代器。
        array.rend(); //返回容器中倒数最后一个元素的后面的迭代器。
        array.cbegin(); //返回容器中第一个数据的常量迭代器。
        array.cend(); //返回容器中最后一个数据之后的常量迭代器。
        array.crbegin(); //返回容器中倒数第一个元素的常量迭代器。
        array.crend(); //返回容器中倒数最后一个元素的后面的常量迭代器。

 C++的类型转换

TYPE b = 类型操作符<TYPE> ( a )
类型操作符= static_cast | reinterpreter_cast | dynamic_cast | const_cast       

 static_cast

        静态类型转换(斯文的劝导,温柔的转换)。如 int 转换成 char
主要用法:
        1: 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。上行指针或引用(派生类到基类)转换安全,由于没有动态类型检查,所以下行是不安全的 ,静态转换用于已知类型的安全转换, 当你有一个基类指针或引用指向一个派生类对象,但你知道该对象的确切类型时,可以使用静态转换将基类指针或引用转换为派生类指针或引用
        2: 用于基本数据类型之间的转换,如把 int 转换成 char ,把 int 转换成 enum。这种转换的安全性也要开发人员来保证。
        3: 把空指针转换成目标类型的空指针。
        4: 把任何类型的表达式转换成 void 类型。
        double i1 = 1.09;
        int i2 = static_cast<int> i1;
#include <iostream>
using namespace std;
class Animal {
public:
    virtual void cry() = 0;
};

class cat :public Animal{
public:
    void cry() {
        cout << "喵喵喵!" << endl;
    }
};
class dog :public Animal{
public:
    void cry() {
        cout << "汪汪汪!" << endl;
    }
};

int main(void) {
    //父子之间的转换指针
    dog* dog1 = new dog();
    Animal* a1 = static_cast<Animal*>(dog1);//将子类的指针转到父类
    a1->cry();
    dog* dog2 = static_cast<dog*>(a1);//将父类的指针转到子类
    cat* cat1 = static_cast<cat*>(a1);//父亲指向子类的指针不要再指向另外一个子类,有风险
    cat1->cry();
            //父子之间的转换引用
    dog dog3;
    Animal& a2 = static_cast<Animal&>(dog3); //将子类的引用转到父类
    dog& dog4 = static_cast<dog&>(a2);//将父类引用转到子类
    //基础类型转换
    int k = 11;
    char s = static_cast<char>(k);
    //吧空指针转换成目标类型的空指针
    int* i = static_cast<int*>(NULL);
    dog* dog5 = static_cast<dog*>(NULL);

    //将任何类型的表达式转换成void
    int* pi = new int[10];
    void* vp = static_cast<void*>(pi);
    vp = pi;//隐式
}

 reinterpret_cast

        重新解释类型( 挂羊头,卖狗肉 ) 不同类型间的互转,数值与指针间的互转
用法: TYPE b = reinterpret_cast <TYPE> ( a )
TYPE 必须是一个指针、引用、算术类型、函数指针 .
        忠告:滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低
级别的,否则应使用其他强制转换运算符之一。
#include <iostream>
using namespace std;
class Animal {
public:
    virtual void cry() = 0;
};

class cat :public Animal {
public:
    void cry() {
        cout << "喵喵喵!" << endl;
    }
};
class dog :public Animal {
public:
    void cry() {
        cout << "汪汪汪!" << endl;
    }
};

int main(void) {
    //用法一 数值与指针之间的转换
    int* p = reinterpret_cast<int*>(0x99999);
    int val = reinterpret_cast<int>(p);
    cout << val << endl;
    //用法二 不同类型指针和引用之间的转换
    dog dog1;
    Animal* a1 = &dog1;
    a1->cry();
    dog* dog1_p = reinterpret_cast<dog*>(a1);
    dog* dog2_p = static_cast<dog*>(a1); //如果能用 static_cast ,static_cast 优先
    //Cat* cat1_p = static_cast<Cat*>(a1);
    //Cat* cat2_p = static_cast<Cat*>(dog1_p);//NO! 不同类型指针转换不能使用 static_cast
    cat* cat2_p = reinterpret_cast<cat*>(dog1_p);
    Animal& a2 = dog1;
    dog& dog3 = reinterpret_cast<dog&>(a2);//引用强转用法
    dog1_p->cry();
    dog2_p->cry();
    cat2_p->cry();
    system("pause");
    return 0;
}

 dynamic_cast

动态类型转换
         而动态转换用于运行时类型检查,以确保在向下转型时的安全性。

        动态转换是在运行时进行的转换,它用于在类层次结构中安全地进行向下转型。它会在运行时检查对象的实际类型,如果转换是安全的,则返回正确的指针;否则,返回空指针。

        将一个基类对象指针 cast 到继承类指针, dynamic_cast 会根据基类指针是否真正指向继承类指针来做相应处理。失败返回 null ,成功返回正常 cast 后的对象指针;
        将一个基类对象引用 cast 继承类对象, dynamic_cast 会根据基类对象是否真正属于继承类来做相应处理。 失败抛出异常 bad_cast
        注意:dynamic_cast 在将父类 cast 到子类时, 父类必须要有虚函数一起

#include <iostream>
using namespace std;
class Animal {
public:
    virtual void cry() = 0;
};

class cat :public Animal {
public:
    void cry() {
        cout << "喵喵喵!" << endl;
    }
};
class dog :public Animal {
public:
    void cry() {
        cout << "汪汪汪!" << endl;
    }
};
void testAnimal(Animal* animal) {
    animal->cry();
    dog* dog1 = dynamic_cast<dog*>(animal);
    if (dog1) {
        dog1->cry();
    }
    cout << endl;
    cat* cat1 = dynamic_cast<cat*>(animal);
    if (cat1) {
        cat1->cry();
    }
}
void testAnimal1(Animal& animal) {
    animal.cry();

    try {
        dog& dog1 = dynamic_cast<dog&>(animal);
    }
    catch (const std::bad_cast) {
        cout << "是猫不是狗,喵喵喵" << endl;
    }

    try {
        cat& cat1 = dynamic_cast<cat&>(animal);
    }
    catch (const std::bad_cast) {
        cout << "是狗不是猫,汪汪汪" << endl;
    }
}

int main(void) {
    string line(50,' _');
    dog *dog1 = new dog();
    Animal* a1 =  dog1;
    testAnimal(a1);
    cout << line << endl;
    cat* cat1= new cat();
    Animal* a2 = cat1;
    testAnimal(a2);
    cout << line << endl;
    dog dog2;
    Animal& a3 = dog2;
    testAnimal1(a3);
    cout << line << endl;
    cat cat2;
    Animal& a4 = cat2;
    testAnimal1(a4);
}

 const_cast

去除const属性,仅针对指针和引用

#include <iostream>
using namespace std;
void demo(const char* p) {
    //对指针去掉 cost 重新赋值
    //char* p1 = const_cast<char *>(p);
    //p1[0] = 'A';
    //直接去掉 const 修改
    const_cast<char*>(p)[0] = 'A';
    cout << p << endl;
}
void demo1(const int p) {
    int q = p;
    //const_cast<int>(p) = 888;// NO ! 不能对非指针和引用进行 const 转换
    cout << p << endl;
}
int main(void) {
    //字符串数组
    char p[] = "12345678";
    //demo(p); //合情合理
    //常量字符串是在常量区,const_cast不能进行去掉 const 修改
    //警告: 在去掉常量限定符之前,保证指针所指向的内存能够修改,不能修改则会引起异常。
    //const char* cp = "987654321";
    demo(p);
    system("pause");
    return 0;
}

 C++智能指针

string* str = new string("这个世界到处是坑,所以异常处理要谨记在心!!!");delete str; //手动释放内存

string str("这个世界到处是坑,所以异常处理要谨记在心!!!");//自动释放内存

智能指针:   分配的动态内存都交由有生命周期的对象 来处理,那么在对象过期时,让它的析构函数删除指向的 内存
C++98 提供了 auto_ptr 模板的解决方案
C++11 增加 unique_ptr shared_ptr weak_ptr

 auto_ptr 使用详解 (C++98)

 auto_ptr 使用详解 (C++98)

其定义了管理指针的对象,可以将 new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使 用 delete 来释放内存!
头文件: #include <memory>
用 法: auto_ptr<类型> 变量名(new 类型)
auto_ptr<string> str(new string("我要成为大牛~ 变得很牛逼!"));
auto_ptr<vector<int>> auto_v(new vector<int>(10));
使用建议
1.尽可能不要将 auto_ptr 变量定义为全局变量或指针
2.除非自己知道后果,不要把 auto_ptr 智能指针赋值给同类型的另外一个
智能指针
3.C++11 后 auto_ptr 已经被“抛弃”,已使用 unique_ptr 替代!

 

#include <iostream>
#include <memory.h>
using namespace std;
class test {
public:
    test() { cout << "调用构造函数" << endl;
    id = 1;
    }
    ~test() { cout<<"调用析构函数"<<endl; }
    int getId() {
        return id;
    }
private:
    int id;
};

void test1() {
    auto_ptr<test> t(new test());//会自动调用析构函数
    //test *str = new test();//申请的空间不会自动释放
    t->getId();//调用和str一样,auto_ptr重载了->符的
    (*t).getId();//
   //test *temp = t.release();//取消智能指针的自动释放
   //delete temp;
    //t.reset(new test());//重置智能指针托管的内存地址
    //建议1:   尽量不要定义为全局变量(没有意义)
    //建议2:   建议不要将会一个智能指针赋值或者指向另外一个智能指针
    //auto_ptr<test>* it = new auto_ptr<test>(new test());
    //建议3:   建议不要将将一个智能指针赋值给另外一个智能指针
    //auto_ptr<test> it;
    //t = it;
}
int main(void) {
    test1();

    return 0;
}

 unique_ptr 使用详解 (C++11)单个

 unique_ptr 使用详解 (C++11)

        auto_ptr 是用于 C++11 之前的智能指针。由于 auto_ptr 基于排他所有权模式: 两个指针不能指向同一个资源,复制或赋值都会改变资源的所有权。auto_ptr 主 要有两大问题:
        复制和赋值会改变资源的所有权,不符合人的直觉。
        在 STL 容器中使用 auto_ptr 存在重大风险,因为容器内的元素必需支持可复制 (copy constructable)和可赋值(assignable)。
        不支持对象数组的操作

 auto_ptr的弊端

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

int main(void) {
    auto_ptr<string> p1(new string("I am a student"));
    auto_ptr<string>p2(new string("I am not a teacher"));
    cout << "p1的地址: " << p1.get() << endl;
    cout << "p2的地址: " << p2.get() << endl;
    //p1 = p2之后.p2把自己的内存都地址都交给了p1,p1则丢弃了原本的(弊端1)
    p1 = p2;
    cout << endl;
    cout << "p1 = p2之后" << endl;
    cout << "p1的地址: " << p1.get() << endl;
    cout << "p2的地址: " << p2.get() << endl;
    auto_ptr虽然有排他性,但是使用reset还是可以指向同一块内存
    //string* str = new string("内存重复管理错误");
    //{
    //    //这个功能块结束之后auto_ptr会将str自动释放
    //    auto_ptr<string> p3;
    //    p3.reset(str);
    //}
    //auto_ptr<string> p4;
    放入了已经被释放的str,就会出现访问权限的bug
    //p4.reset(str);
    //这种在STL容器中就会出现问题,在STL中必须支持可复制和赋值
    //那么如果使用auto_str的指针去赋值就会出先问题(弊端2)
    //vector<auto_ptr<string>> va;

    //auto_ptr<string> v1(new string("Hello1"));
    //auto_ptr<string> v2(new string("Hello2"));
    //va.push_back(v1);
    //va.push_back(v2);
    //cout << *va[0];
    //cout << *va[1];
    这里将va[0]赋值给va[1]之后
    //va[1] = va[0];
    // cout << *va[0];
    //cout << *va[1];
    //不支持数组管理(弊端3)
    //auto_ptr<int[]> vv(new int[15]);

    return 0;
}

unique_ptr严谨auto_ptr的弊端

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

int main(void) {
    unique_ptr<string> p1(new string("I am a student"));
    unique_ptr<string> p2(new string("I am not a teacher"));
    cout << "p1的地址: " << p1.get() << endl;
    cout << "p2的地址: " << p2.get() << endl;
    //unique_str直接不支持这样赋值(针对弊端1 :增加了可读性)
    //p1 = p2;
    //如果一定要转移,使用 move 把左值转成右值
    //同auto_ptr一样
    //p1 = p2之后.p2把自己的内存都地址都交给了p1,p1则丢弃了原本的
    p1 = move(p2);
    cout << "p1的地址: " << p1.get() << endl;
    cout << "p2的地址: " << p2.get() << endl;
    //auto_ptr虽然有排他性,但是使用reset还是可以指向同一块内存
    // unique_str依然存在这种问题
    //string* str = new string("内存重复管理错误");
    //{
    //    //这个功能块结束之后auto_ptr会将str自动释放
    //    unique_ptr<string> p3;
    //    p3.reset(str);
    //}
    //unique_ptr<string> p4;
    放入了已经被释放的str,就会出现访问权限的bug
    //p4.reset(str);
    //这种在STL容器中就会出现问题,在STL中必须支持可复制和赋值
    //那么如果使用unique_ptr的指针解决直接问允许直接赋值(针对弊端2: 不允许赋值)
    //vector<unique_ptr<string>> va;

    //unique_ptr<string> v1(new string("Hello1"));
    //unique_ptr<string> v2(new string("Hello2"));
    //va.push_back(v1);
    //va.push_back(v2);
    //cout << *va[0];
    //cout << *va[1];
    这里将va[0]赋值给va[1]之后
    //va[1] = va[0];//无法赋值
    // cout << *va[0];
    //cout << *va[1];
    //unique_ptr支持数组对象(针对弊端3: 支持数组)
    unique_ptr<int[]> vv(new int[15]);//会自动调用delete[] 去释放

    return 0;
}

unique_ptr和auto_ptr都不支持同一内存交给多个变量

     //auto_ptr虽然有排他性,但是使用reset还是可以指向同一块内存
     // unique_str依然存在这种问题
     //string* str = new string("内存重复管理错误");
     //{
     //    //这个功能块结束之后auto_ptr会将str自动释放
     //    unique_ptr<string> p3;
     //    p3.reset(str);
     //}
    //unique_ptr<string> p4;
    放入了已经被释放的str,就会出现访问权限的bug
    //p4.reset(str);

unique_ptr 特性 

构造函数
        unique_ptr<T> up ; // 空的 unique_ptr ,可以指向类型为 T 的对象
        unique_ptr<T> up1(new T()) ;//定义 unique_ptr, 同时指向类型为 T 的对象
        unique_ptr<T[]> up ; // 空的 unique_ptr ,可以指向类型为 T[ 的数组对象
        unique_ptr<T[]> up1(new T[]) ;//定义 unique_ptr, 同时指向类型为 T 的数组对象
        unique_ptr<T,D> up(); // 空的 unique_ptr, 接受一个 D 类型的删除器 d, 使用 d 释放内存
        unique_ptr<T,D> up(new T()); // 定义 unique_ptr, 同时指向类型为 T 的对象,接受一个 D 类型的删除器 d ,使用删除器 d 来释放内存

#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class test {
public:
    test() { cout << "调用构造函数" << endl;
    id = 1;
    }
    ~test() { cout<<"调用析构函数"<<endl; }
    int getId() {
        return id;
    }
    void dosomething() {
        cout << "做了一些事情" << endl;
    }
private:
    int id;
};
class del{
public:
    void operator()(test *pt) {
        cout << "进去删除器了" << endl;
        delete pt;//这里进行删除
    }
};
int main(void) {

    unique_ptr<test[]> vv(new test[5]);//会自动调用delete[] 去释放
    //指定删除器
    cout << "指定删除器" << endl;
    unique_ptr<test, del> del(new test());
    return 0;
}

 

赋值
        unique_ptr<int> up1(new int(10));
        unique_ptr<int> up2(new int(11));
        up1 = std::move(up2);//必须使用 移动语义, 结果 ,up1 内存释放 , up2 交由 up1 管理
主动释放对象auto_ptr并不支持这种
        up = nullptr ;//释放 up 指向的对象,将 up 置为空 或 up = NULL; //作用相同
放弃对象控制权
        up.release(); //放弃对象的控制权,返回指针,将 up 置为空,不会释放内存
重置
up.reset( ) // 参数可以为 空、内置指针,先将 up 所指对象释放,然后重置 up 的值
交换
up.swap(up1); // 将智能指针 up up1 管控的对象进行交换

shared_ptr 使用详解 (C++11)多个

 

可以记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加 1,当智能指针析构时,引用计数减 1,如果计数为零,代表已经没 有指针指向这块内存,那么我们就释放它!

 构造函数

        shared_ptr<T> sp ; // 空的 shared_ptr ,可以指向类型为 T 的对象
        shared_ptr<T> sp1(new T()) ;//定义 shared_ptr, 同时指向类型为 T 的对象
        shared_ptr<T[]> sp2 ; // 空的 shared_ptr, 可以指向类型为 T[的数组对象 C++17 后支持
        shared_ptr<T[]> sp3(new T[]{...}) ;// 指向类型为 T 的数组对象 C++17 后支持
        shared_ptr<T> sp4(NULL, D()); // 空的 shared_ptr ,接受一个 D 类型的删除器,使用 D 释放内存
        shared_ptr<T> sp5(new T(), D()); // 定义 shared_ptr, 指向类型为 T 的对象,接受一个 D
类型的删除器,使用 D 删除器来释放内存

 初始化

方式一 构造函数
        shared_ptrr<int> up1(new int(10)); //int(10) 的引用计数为 1
        shared_ptrr<int> up2(up1); //使用智能指针 up1 构造 up2, 此时 int(10) 引用计数为 2
方式二 使用 make_shared 初始化对象,分配内存效率更高
         make_shared 函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的 shared_ptr; 用法:
make_shared< 类型 > ( 构造类型对象需要的参数列表 ) ;
        shared_ptr<int> p4 = make_shared<int>(2); // 多个参数 以逗号 ',' 隔开,最多接受十个
        shared_ptr<string> p4 = make_shared<string>("字符串 ");

 赋值

        shared_ptrr<int> up1(new int(10)); //int(10) 的引用计数为 1
        shared_ptr<int> up2(new int(11)); //int(11) 的引用计数为 1
        up1 = up2;//int(10) 的引用计数减 1, 计数归零内存释放, up2 共享 int(11)给 up1,int(11)                 的引用计数为 2
主动释放对象
        shared_ptrr<int> up1(new int(10));
        up1 = nullptr ;//int(10) 的引用计数减 1, 计数归零内存释放 或 up1 = NULL; //作用同上
                重置
        up.reset() ; // p 重置为空指针,所管理对象引用计数 减 1
        up.reset(p1); // p 重置为 p1 (的值) ,p 管控的对象计数减 1, p 接管对 p1 指针的管控
        up.reset(p1,d); // p 重置为 p (的值), p 管控的对象计数减 1 并使用 d 作为删除器
交换
        std::swap(p1,p2); //交换 p1 p2 管理的对象,原对象的引用计数不变
        p1.swap(p2); //同上

 

#include <iostream>
#include <share.h>
using namespace std;

class test {
public:
    test(int id) { 
        cout << "调用构造函数" <<id<< endl;
        this->id = id;
    }
    ~test() { cout<<"调用析构函数"<<id<<endl; }
    int getId() {
        return id;
    }
    void dosomething() {
        cout << "做了一些事情" << endl;
    }
private:
    int id;
};
class del {
public:
    void operator()(test* pt) {
        cout << "进去删除器了" << endl;
        pt->dosomething();
        delete pt;//这里进行删除
    }
};
int main(void) {
    string line(50, '_');
    shared_ptr<test> p1;//空的share_ptr
    shared_ptr<test> p2(new test(2));//指向对象test的共享指针
    //p2.use_count();//当前管控的p2的数量
    //共享的同一个计数器,
    cout << "p1的管控数量:" << p1.use_count() << endl;
    cout << "p2的管控数量:" << p2.use_count() << endl;
    cout << "将p2赋值给p1之后:" << endl;
    p1 = p2;
    cout << "p1的管控数量:" << p1.use_count() << endl;
    cout << "p2的管控数量:" << p2.use_count() << endl;
    cout << "调用拷贝构造之后:" << endl;
    shared_ptr<test> p3(p1);
    cout << "p1的管控数量:" << p1.use_count() << endl;
    cout << "p2的管控数量:" << p2.use_count() << endl;
    cout << "p3的管控数量:" << p3.use_count() << endl;
    //释放
    {
        cout << "使用释放之后的管控数量" << endl;
        p1 = NULL;
        cout << "p1的管控数量:" << p1.use_count() << endl;
        cout << "p2的管控数量:" << p2.use_count() << endl;
        cout << "p3的管控数量:" << p3.use_count() << endl;
    }
    cout << "使用reset重置管控数量" << endl;
    test * p7 = new test(6);
    p2.reset(p7);
    cout << "p2的管控数量:" << p2.use_count() << endl;
    cout << p7->getId() << endl;
    //数组
    cout << line << endl;
    cout<<"使用数组"<<endl;
    {
        shared_ptr<test[]> p4(new test[5]{2,3,4,5,6});//C++11以上支持
        cout << "p5的管控数量:" << p4.use_count() << endl;
    }
   
    //使用删除器
    cout << line << endl;
    cout << "使用删除器" << endl;
    //shared_ptr<test> p5(del());
    shared_ptr<test> p5(new test(3),del());
    //初始化
    cout << line << endl;
    cout << "使用make_shared分配" << endl;
    shared_ptr<test> p6 = make_shared<test>(4);
    cout << p6->getId() << endl;
    cout << "p6的管控数量:" << p6.use_count() << endl;
    //使用陷阱


    return 0;
}

weak_ptr 使用详解 (自从 C++11)

 shared_ptr的陷阱问题(没析构)

#include <iostream>
#include <share.h>

using namespace std;

class Girl;
class Boy {
public:
    Boy() {
        cout << "男孩,构造了" << endl;
    };
    ~Boy() {
        cout << "男孩,析构了" << endl;
    }
    void set_girl(shared_ptr<Girl>& g) {
        boys_girl = g;//girl管控数量+1
    }
private:
    shared_ptr<Girl> boys_girl;
};

class Girl {
public:
    Girl() {
        cout << "女孩,构造了" << endl;
    };
    ~Girl() {
        cout << "女孩,析构了" << endl;
    }
    void set_girl(shared_ptr<Boy> &b) {
        girls_boy = b; //boy管控数量 + 1
    }
private:
    shared_ptr<Boy> girls_boy;
};

void setFriend() {

    shared_ptr<Boy> boy(new Boy());//boy管控数量+1
    shared_ptr<Girl> girl(new Girl());//girl管控数量+1
    boy->set_girl(girl);
    girl->set_girl(boy);
    //释放的时候 管控数量只释放了一次,但是在调用的时候还产生了一次管控数量
    //所有还不能析构
}

int main(void) {

    setFriend();
    return 0;
}

 weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作,它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少. 同时 weak_ptr 没有重载* 和->但可以使用 lock 获得一个可用的 shared_ptr 对象。

 

#include <iostream>
#include <share.h>

using namespace std;

class Girl;
class Boy {
public:
    Boy() {
        cout << "男孩,构造了" << endl;
    };
    ~Boy() {
        cout << "男孩,析构了" << endl;
    }
    void set_girl(shared_ptr<Girl>& g) {
        boys_girl = g;//girl管控数量+1
    }
private:
    //shared_ptr<Girl> boys_girl;
    weak_ptr<Girl> boys_girl;//设置为弱指针//lock
};

class Girl {
public:
    Girl() {
        cout << "女孩,构造了" << endl;
    };
    ~Girl() {
        cout << "女孩,析构了" << endl;
    }
    void set_girl(shared_ptr<Boy> &b) {
        girls_boy = b; //boy管控数量 + 1
    }
private:
    shared_ptr<Boy> girls_boy;
    //weak_ptr<Boy> girls_boy;//设置为弱指针//lock
};

void setFriend() {

    shared_ptr<Boy> boy(new Boy());//boy管控数量+1
    shared_ptr<Girl> girl(new Girl());//girl管控数量+1
    cout << boy.use_count() << endl;
    cout << girl.use_count() << endl;
    
    //{
    //    //弱指针的使用,相当于就是一个记录
    //    weak_ptr<Boy> wp_boy;
    //    //weak_ptr<Girl> wp_girl;
    //    //在必要的时候又可以转为共享指针
    //    shared_ptr<Boy> boy1;
    //    wp_boy = boy;//wp_boy接收boy管控之后并不会将管控数量+1
    //    cout <<"wb_boy赋值后"<< wp_boy.use_count() << endl;
    //    cout << "boy1的管控数量:" << boy1.use_count() << endl;
    //    boy1 = wp_boy.lock();//将wp_boy也管控boy1之后
    //    cout <<"boy1接收wp_boy的管控之后"<< boy1.use_count() << endl;
    //    cout << "wb_boy管控boy1后" << wp_boy.use_count() << endl;
    //    boy1 = NULL;
    //    cout << "boy1释放之后" << boy1.use_count() << endl;
    //    cout << "wb_boy管控的boy1释放之后后" << wp_boy.use_count() << endl;
    //}
    boy->set_girl(girl);
    girl->set_girl(boy);
    cout << boy.use_count() << endl;
    cout << girl.use_count() << endl;
        //释放的时候 管控数量只释放了一次,但是在调用的时候还产生了一次管控数量
        //所有还不能析构
    //system("pause");
}

int main(void) {

    setFriend();
    return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1408108.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开始学习Vue2(组件的生命周期和数据共享)

一、组件的生命周期 1. 生命周期 & 生命周期函数 生命周期&#xff08;Life Cycle&#xff09;是指一个组件从创建 -> 运行 -> 销毁的整个阶段&#xff0c;强调的是一个时间段。 生命周期函数&#xff1a;是由 vue 框架提供的内置函数&#xff0c;会伴随着 组件…

luceda ipkiss教程 57:画微环调制器

案例分享&#xff1a;画微环调制器 全部代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3class DC(i3.PCell):straight_length i3.PositiveNumberProperty(default200)radius i3.PositiveNumberProperty(default50)spacing i3.Positive…

推荐系统算法 协同过滤算法详解(二)皮尔森相关系数

目录 前言 协同过滤算法(简称CF) 皮尔森(pearson)相关系数公式 算法介绍 算法示例1&#xff1a; 算法示例2 前言 理解吧同胞们&#xff0c;实在是没办发把wps公式复制到文章上&#xff0c;只能截图了&#xff0c;我服了&#xff01;&#xff01;&#xff01; 协同过滤算法…

基于中文垃圾短信数据集的经典文本分类算法实现

垃圾短信的泛滥给人们的日常生活带来了严重干扰&#xff0c;其中诈骗短信更是威胁到人们的信息与财产安全。因此&#xff0c;研究如何构建一种自动拦截过滤垃圾短信的机制有较强的实际应用价值。本文基于中文垃圾短信数据集&#xff0c;分别对比了朴素贝叶斯、逻辑回归、随机森…

数据结构——排序算法代码实现、包含注释易理解可运行(C语言,持续更新中~~)

一、排序 1.1 直接插入排序 1.1.1 思想 插入排序的核心操作是将待排序元素与已排序序列中的元素进行比较&#xff0c;并找到合适的位置进行插入。这个过程可以通过不断地将元素向右移动来实现。 插入排序的优势在于对于小规模或基本有序的数组&#xff0c;它的性能非常好。…

【经验分享】豆瓣小组的文章/帖子怎么删除?

#豆瓣小组的文章/帖子怎么删除&#xff1f;# 第一步&#xff1a; 手机登录豆瓣app ↓ 点右下角“我” ↓ 然后在页面点击我的小组 ↓ 点我发布的 ↓ ↓ 再任意点开一个帖子 ↓ 在文章和帖子的右上角有一个笔状的图标&#xff0c;切记不是右上角的横三点… ↓ ↓ 最后点下边的…

odoo 一日一技 owl Registry示例 在用户菜单增加开发者模式开关

# 示例介绍 在Odoo中&#xff0c;开发者模式是一个非常有用的工具&#xff0c;它允许开发人员对系统进行调试。如果每次都要去设置中打开调试模式将非常麻烦&#xff0c;上篇文章讲述了如何使用 owl registry&#xff0c;这篇我们来进行实操。 本文将介绍如何在Odoo的用户菜单…

令人感动的创富故事编号001:27岁Python程序员年入$600万+

27岁Python程序员年入$600万 27岁的你&#xff0c;在做什么&#xff1f; 为家庭生计而努力搬砖&#xff0c;辛勤工作&#xff1f; 还是放弃挣扎&#xff0c;选择躺平呢&#xff1f; 当我们还在为未来道路感到困惑之际&#xff0c;年仅27岁的Reilly已经迈向了财富自由的大门…

Socket 文件描述符

文件描述符的作用是什么&#xff1f; 每一个进程都有一个数据结构 task_struct&#xff0c;该结构体里有一个指向「文件描述符数组」的成员指针。该数组里列出这个进程打开的所有文件的文件描述符。数组的下标是文件描述符&#xff0c;是一个整数&#xff0c;而数组的内容是一…

用VR技术让党建“活起来”,打造党建知识科普新体验

随着现在工作、生活的信息化、网络化持续加深&#xff0c;传统的党建科普对年轻党员的吸引力日益降低&#xff0c;不管是面授讲课还是实地观摩的方式&#xff0c;都会受到时间和空间上的限制。因此&#xff0c;VR数字党建的出现为党建知识科普提供了新的可能&#xff0c;VR党建…

STM32 USB CDC协议的应用与优化技巧

STM32微控制器提供了使用USB CDC&#xff08;Communications Device Class&#xff09;协议来实现虚拟串口通信的功能。USB CDC协议可以将STM32设备模拟为一个虚拟串口设备&#xff0c;并通过USB接口与计算机进行通信。在本文中&#xff0c;我们将介绍USB CDC协议的应用与优化技…

elment-plus如何引入scss文件实现自定义主题色

elment-plus如何引入scss文件实现自定义主题色&#xff01;如果您想修改elementPlus的默认主题色调&#xff0c;使用自定义的色调&#xff0c;可以考虑使用官方提供的解决办法。 第一步你需要在项目内安装sass插件包。 npm i sass -D 如图&#xff0c;安装完成后&#xff0c;你…

[pytorch入门] 6. 神经网络

基本介绍 torch.nn&#xff1a; Containers&#xff1a;基本骨架Convolution Layers&#xff1a; 卷积层Pooling layers&#xff1a;池化层Non-linear Activations (weighted sum, nonlinearity)&#xff1a;非线性激活Normalization Layers&#xff1a;正则化层 Container…

边缘计算及相关产品历史发展

边缘计算及相关产品历史发展 背景边缘计算的历史CDN&#xff08;Content Delivery Network&#xff09;Cloudlet雾计算MEC&#xff08;Multi-Access Edge Computing&#xff0c;MEC&#xff09; 边缘计算的现状云计算厂商硬件厂商软件基金会 背景 最近&#xff0c;公司部分业务…

基于springboot+vue的社区医院信息平台系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

阿赵UE学习笔记——解决UE资源不能正常显示缩略图的问题

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   这里分享一个虚幻引擎使用小技巧。在使用虚幻引擎的过程中&#xff0c;经常会遇到有些资源在重新打开项目的时候&#xff0c;会看不到缩略图&#xff0c;而是显示默认资源的图标&#xff1a; 这个时候&#xff0c;第一种…

应用app的服务器如何增加高并发

增强服务器的高并发能力是现代网络应用非常关键的需求。面对用户数量的不断增长和数据量的膨胀&#xff0c;服务器必须能够处理大量并发请求。以下是一些提高服务器高并发能力的常用方法和具体实施细节&#xff1a; 优化服务器和操作系统配置 服务器和操作系统的默认配置不一定…

快速上手的AI工具-文心一言绘画达人

前言 大家好&#xff0c;现在AI技术的发展&#xff0c;它已经渗透到我们生活的各个层面。对于普通人来说&#xff0c;理解并有效利用AI技术不仅能增强个人竞争力&#xff0c;还能在日常生活中带来便利。无论是提高工作效率&#xff0c;还是优化日常任务&#xff0c;AI工具都可…

【模拟通信】AM、FM等的调制解调

调制相关的概念 调制&#xff1a;控制载波的参数&#xff0c;使载波参数随调制信号的规律变化 已调信号&#xff1a;受调载波&#xff0c;含有调制信号的全部特征 调制的作用: 提高发射效率多路复用&#xff0c;提高信道利用率提高系统抗干扰能力 两种调制方式 线性调制&a…

网络协议与攻击模拟_08DHCP协议

技术学习要了解某项技术能干什么&#xff1f;它的详细内容&#xff1f;发展走向&#xff1f; 一、DHCP协议 1、DHCP基本概念 dhcp动态主机配置协议&#xff0c;广泛应用于局域网内部 主要是为客户机提供TCP/IP 参数&#xff08;IP地址、子网掩码、网关、DNS等&#xff09;…