彻底搞懂之C++智能指针

前言

在现代 c + + 编程中,标准库包含 智能指针,这些指针用于帮助确保程序不会出现内存和资源泄漏,并具有异常安全。

标准库智能指针分类

auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被c++11弃用。所以我只说后3个。

  1. shared_ptr
    采用引用计数的智能指针。 如果你想要将一个原始指针分配给多个所有者(例如,从容器返回了指针副本又想保留原始指针时),请使用该指针。 直至所有 shared_ptr 所有者超出了范围或放弃所有权,才会删除原始指针。 大小为两个指针;一个用于对象,另一个用于包含引用计数的共享控制块。 头文件:
  2. unique_ptr
    只允许基础指针的一个所有者。 除非你确信需要 shared_ptr,否则请将该指针用作 POCO 的默认选项。 可以移到新所有者,但不会复制或共享。 替换已弃用的 auto_ptr。 与 boost::scoped_ptr 比较。 unique_ptr 很小且高效;大小是一个指针,它支持用于从 c + + 标准库集合快速插入和检索的右值引用。 头文件:
  3. weak_ptr
    结合 shared_ptr 使用的特例智能指针。 weak_ptr 提供对一个或多个 shared_ptr 实例拥有的对象的访问,但不参与引用计数。 如果你想要观察某个对象但不需要其保持活动状态,请使用该实例。 在某些情况下,需要断开 shared_ptr 实例间的循环引用。 头文件:

shared_ptr

shared_ptr 类型是 C++ 标准库中的一个智能指针,是为多个所有者可能必须管理对象在内存中的生命周期的方案设计的。 在您初始化一个 shared_ptr 之后,您可复制它,按值将其传入函数参数,然后将其分配给其他 shared_ptr 实例。 所有实例均指向同一个对象,并共享对一个”控制块”(每当新的 shared_ptr 添加、超出范围或重置时增加和减少引用计数)的访问权限。 当引用计数达到零时,控制块将删除内存资源和自身。

下图显示了指向一个内存位置的几个 shared_ptr 实例。

彻底搞懂之C++智能指针

原始用法:

Object * obj = new ChildObject(9);//从heap分配原始父对象,必须手动触发析构, 但子对象不会释放
testObject(*obj);
printf("release9 %p \n", obj);
delete obj;

当testObject()出现异常时,delete将不被执行,因此将导致内存泄露。

如何避免这种问题?有人会说,这还不简单,直接在 throw exception(); 在catch中加上 delete ps;不就行了。问题是很多人都会忘记在适当的地方加上delete语句,如果你要对一个庞大的工程进行review,看是否有这种潜在的内存泄露问题,那就是一场灾难!
这时我们会想: 如果指向heap的内存也能像stack变量一样用完时被自动释放,那该有多好啊。

这正是 auto_ptr、unique_ptr和shared_ptr这几个智能指针背后的设计思想。我简单的总结下就是: 将基本类型指针封装为类对象指针(这个类肯定是个模板,以适应不同基本类型的需求),并在析构函数里编写delete语句删除指针指向的内存空间。

使用shared_ptr:

{
    std::shared_ptr<object> sObj = std::make_shared<childobject>(1);
    testObject(*sObj); //&#x8C03;&#x7528;&#x7236;&#x5BF9;&#x8C61;
    //&#x81EA;&#x52A8;&#x56DE;&#x6536;
}</childobject></object>

很简单对吧~

unique_ptr

unique_ptr不共享指针。 它不能复制到另一个 unique_ptr函数,由值传递给函数,或在任何需要复制副本的 C++ 标准库算法中使用。 只能移动 unique_ptr。 这意味着,内存资源所有权将转移到另一 unique_ptr,并且原始 unique_ptr 不再拥有此资源。 我们建议你将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。 因此,当需要纯 C++ 对象的智能指针时,请使用make_unique帮助程序函数。

下图演示了两个 unique_ptr 实例之间的所有权转换。

彻底搞懂之C++智能指针

unique_ptr 在 C++ 标准库的标头中 <memory></memory> 定义。 它与原始指针一样高效,可在 C++ 标准库容器中使用。 将实例添加到 unique_ptr C++ 标准库容器是有效的,因为移动构造函数 unique_ptr 无需复制操作。

unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权,包括:

1、拥有它指向的对象

2、无法进行复制构造,无法进行复制赋值操作。即无法使两个unique_ptr指向同一个对象。但是可以进行移动构造和移动赋值操作

3、保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象

用法:

std::unique_ptr<int>p1(new int(5));
std::unique_ptr<int>p2=p1;// &#x7F16;&#x8BD1;&#x4F1A;&#x51FA;&#x9519;
std::unique_ptr<int>p3=std::move(p1);// &#x8F6C;&#x79FB;&#x6240;&#x6709;&#x6743;,&#x90A3;&#x5757;&#x5185;&#x5B58;&#x5F52;p3&#x6240;&#x6709;, p1&#x6210;&#x4E3A;&#x65E0;&#x6548;&#x7684;&#x9488;.

p3.reset();//&#x91CA;&#x653E;&#x5185;&#x5B58;.

p1.reset();//&#x65E0;&#x6548;</int></int></int>

share_ptr和unique_ptr的例子:

#include <iostream>
#include <string>
using namespace std;

namespace Test
{
    #define formatBool(b) ((b) ? "true" : "false")
    class Object
    {
    protected:
        int id;

    public:
        using pointer = std::shared_ptr<object>;
        virtual std::string version() const {
            return "1.0.0";
        };
        Object(int _id):id(_id){
            cout << "\nnew parent Object id:" << id  << endl;
        };
        virtual ~Object(){//&#x91CA;&#x653E;&#x65F6;&#xFF0C;&#x9996;&#x5148;&#x662F;&#x6D3E;&#x751F;&#xFF0C;&#x7136;&#x540E;&#x662F;&#x57FA;&#x7C7B;&#x3002;&#x5FC5;&#x987B;&#x5C06;&#x57FA;&#x7C7B;&#x6790;&#x6784;&#x51FD;&#x6570;&#x8BBE;&#x4E3A;&#x865A;&#x57FA;&#x7C7B;&#xFF0C; &#x9632;&#x6B62;delete &#x5B50;&#x5BF9;&#x8C61;&#x65F6;&#x4E0D;&#x4F1A;&#x8C03;&#x7528;&#x7236;&#x6790;&#x6784;&#x51FD;&#x6570;&#xFF0C;&#x5BFC;&#x81F4;&#x5185;&#x5B58;&#x6CC4;&#x9732;
            delete parent_str_ptr;
            cout << "delete parent Object id:" << id  << endl;
        };
        virtual std::string debug() const
        {
            auto str = std::string( "debug Object id:" + std::to_string(id) );
            return str;
        }

    private:
        std::string *parent_str_ptr = new std::string("parent_str_ptr memory leak");
    };
    class ChildObject : public Object
    {
    public:
        ChildObject(int _id):Object(_id)
        {
            std::cout << "new ChildObject id:" << (id) << "\n";
        }

        ~ChildObject()
        {
            delete str_ptr;
            std::cout << "delete ChildObject id:" << id << "\n";
        }
        virtual std::string version() const {
            return "2.0.0";
        };
    private:
        std::string *str_ptr = new std::string("memory leak");

    };

    void testObject(const Object &obj)
    {
        std::cout << obj.debug() << " version:"<< obj.version() << "\n";
    }

    void testCase()
    {
        {
            std::shared_ptr<object> sObj = std::make_shared<childobject>(1);
            testObject(*sObj); //&#x8C03;&#x7528;&#x7236;&#x5BF9;&#x8C61;
            //&#x81EA;&#x52A8;&#x56DE;&#x6536;
        }

        {
            std::unique_ptr<object> obj = std::make_unique<childobject>(2);
            testObject(*obj);
            auto obj2 = std::move(obj);//&#x8F6C;&#x79FB;&#x6240;&#x6709;&#x6743;&#x5230;obj2

            printf("obj:%s obj2:%s \n", formatBool(!!obj), formatBool(!!obj2));

            testObject(*obj2);//&#x8C03;&#x7528;&#x7236;&#x5BF9;&#x8C61;

            obj2.release();//&#x624B;&#x52A8;&#x91CA;&#x653E;&#x540E;&#xFF0C; obj, obj2&#x6307;&#x5411;&#x7684;&#x5BF9;&#x8C61;&#x5DF2;&#x7ECF;&#x88AB;&#x56DE;&#x6536;&#xFF0C; &#x4E0D;&#x4F1A;&#x89E6;&#x53D1;&#x81EA;&#x52A8;&#x56DE;&#x6536;
            printf("obj2.release&#xFF0C; obj:%s obj2:%s \n", formatBool(!!obj), formatBool(!!obj2));
        }

        {
            std::unique_ptr<childobject> obj = std::make_unique<childobject>(3);// &#x4F7F;&#x7528;make_unique
            testObject(*obj);
            printf("release3 %s \n", formatBool(!!obj));
        }
        {
            std::unique_ptr<childobject> obj(new ChildObject(4));//&#x4F7F;&#x7528;new
            testObject(*obj);
            printf("release4 %s \n", formatBool(!!obj));
        }
        {
            // std::unique_ptr<childobject> obj(ChildObject(5));//&#x4F7F;&#x7528;stack&#x5BF9;&#x8C61;&#xFF0C;&#x8FD9;&#x662F;&#x9519;&#x8BEF;&#x7684;&#x7528;&#x6CD5;&#xFF0C; error: no matching constructor for initialization of 'std::unique_ptr<object>'
            // printf("release5 %d \n", !!obj);
        }
        {
            std::unique_ptr<object> obj = std::make_unique<childobject>(6);//&#x7528;&#x7236;&#x5BF9;&#x8C61;, &#x4F1A;&#x89E6;&#x53D1;&#x6790;&#x6784;
            testObject(*obj);
            printf("release6 %s \n", formatBool(!!obj));
        }
        {
            ChildObject obj = ChildObject(7);//&#x4ECE;stack&#x5206;&#x914D;&#x539F;&#x59CB;&#x5BF9;&#x8C61;&#xFF0C; &#x4F1A;&#x89E6;&#x53D1;&#x6790;&#x6784;
            testObject(obj);
            printf("release7 %p \n", &obj);
        }
        {
            ChildObject * obj = new ChildObject(8);//&#x4ECE;heap&#x5206;&#x914D;&#x539F;&#x59CB;&#x5BF9;&#x8C61;&#xFF0C; &#x5FC5;&#x987B;&#x624B;&#x52A8;&#x89E6;&#x53D1;&#x6790;&#x6784;
            testObject(*obj);
            printf("release8 %p \n", obj);
            delete obj;
        }
        {
            Object * obj = new ChildObject(9);//&#x4ECE;heap&#x5206;&#x914D;&#x539F;&#x59CB;&#x7236;&#x5BF9;&#x8C61;&#xFF0C;&#x5FC5;&#x987B;&#x624B;&#x52A8;&#x89E6;&#x53D1;&#x6790;&#x6784;
            testObject(*obj);
            printf("release9 %p \n", obj);
            delete obj;
        }
        {
            Object * obj = new Object(10);//&#x4ECE;heap&#x5206;&#x914D;&#x539F;&#x59CB;&#x7236;&#x5BF9;&#x8C61;&#xFF0C;&#x5FC5;&#x987B;&#x624B;&#x52A8;&#x89E6;&#x53D1;&#x6790;&#x6784;
            testObject(*obj);
            printf("release10 %p \n", obj);
            delete obj;
        }
        {
            std::shared_ptr<object> obj = std::make_unique<childobject>(11);//&#x6307;&#x5411;&#x7236;&#x5BF9;&#x8C61;, &#x4F1A;&#x91CA;&#x653E;&#x5B50;&#x5BF9;&#x8C61;
            testObject(*obj);
            printf("release11 %s \n", formatBool(!!obj));
        }
        // {
        //     std::unique_ptr<object> obj = std::make_shared<childobject>(11);//error: no viable conversion from 'shared_ptr<test::childobject>' to 'std::unique_ptr<object>'
        //     testObject(*obj);
        //     printf("release11 %s \n", formatBool(!!obj));
        // }
    }
}

int main(int argc, char **argv)
{
    Test::testCase();

    return EXIT_SUCCESS;
}</object></test::childobject></childobject></object></childobject></object></childobject></object></object></childobject></childobject></childobject></childobject></childobject></object></childobject></object></object></string></iostream>
 c++ -std=c++14 -o a share_ptr.cpp; ./a
new parent Object id:1
new ChildObject id:1
debug Object id:1 version:2.0.0
delete ChildObject id:1
delete parent Object id:1

new parent Object id:2
new ChildObject id:2
debug Object id:2 version:2.0.0
obj:false obj2:true
debug Object id:2 version:2.0.0
obj2.release&#xFF0C; obj:false obj2:false

new parent Object id:3
new ChildObject id:3
debug Object id:3 version:2.0.0
release3 true
delete ChildObject id:3
delete parent Object id:3

new parent Object id:4
new ChildObject id:4
debug Object id:4 version:2.0.0
release4 true
delete ChildObject id:4
delete parent Object id:4

new parent Object id:6
new ChildObject id:6
debug Object id:6 version:2.0.0
release6 true
delete ChildObject id:6
delete parent Object id:6

new parent Object id:7
new ChildObject id:7
debug Object id:7 version:2.0.0
release7 0x7ff7bfcf3488
delete ChildObject id:7
delete parent Object id:7

new parent Object id:8
new ChildObject id:8
debug Object id:8 version:2.0.0
release8 0x7fcaef705ba0
delete ChildObject id:8
delete parent Object id:8

new parent Object id:9
new ChildObject id:9
debug Object id:9 version:2.0.0
release9 0x7fcaef705ba0
delete ChildObject id:9
delete parent Object id:9

new parent Object id:10
debug Object id:10 version:1.0.0
release10 0x7fcaef705ba0
delete parent Object id:10

new parent Object id:11
new ChildObject id:11
debug Object id:11 version:2.0.0
release11 true
delete ChildObject id:11
delete parent Object id:11

weak_ptr

weak_ptr是用来 解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

最佳设计是避免在任何时候都能实现指针的共享所有权。 但是,如果您必须有实例的 shared_ptr 共享所有权,请避免它们之间存在循环引用。 如果无法避免循环引用,或者出于某种原因更可取,则使用 weak_ptr 向一个或多个所有者提供对另 shared_ptr 一个的弱引用。 通过使用 weak_ptr ,可以创建一个 shared_ptr 联接到一组现有相关实例的,但前提是基础内存资源仍有效。 weak_ptr本身并不参与引用计数,因此它无法阻止引用计数转到零。 但是,你可以使用 weak_ptr 来尝试获取用于初始化的的新副本 shared_ptr 。 如果已删除内存,则的 bool 运算符将 weak_ptr 返回 false 。 如果内存仍有效,新的共享指针会递增引用计数,并保证只要 shared_ptr 变量保持在范围内,内存就有效。weak_ptr是弱智能指针对象,它不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的智能指针。将一个weak_ptr绑定到一个shared_ptr对象,不会改变shared_ptr的引用计数。一旦最后一个所指向对象的shared_ptr被销毁,所指向的对象就会被释放,即使此时有weak_ptr指向该对象,所指向的对象依然被释放。

例子:

#include <iostream>
#include <memory>

class A;

class B
{
public:
    ~B()
    {
        std::cout << "B destory, a_ptr use_count:" << a_ptr.use_count() << "\n";
    }

    //    std::shared_ptr<a> a_ptr; //&#x5B83;&#x4F1A;&#x9020;&#x6210;&#x5FAA;&#x73AF;&#x5F15;&#x7528;
    std::weak_ptr<a> a_ptr;//&#x5B83;&#x4E0D;&#x4F1A;&#x5FAA;&#x73AF;&#x5F15;&#x7528;
};

class A
{
public:
    ~A()
    {
        std::cout << "A destory, b_ptr use_count:" << b_ptr.use_count() << "\n";
    }

    // std::shared_ptr<b> b_ptr;//&#x5B83;&#x4F1A;&#x9020;&#x6210;&#x5FAA;&#x73AF;&#x5F15;&#x7528;
    std::weak_ptr<b> b_ptr;//&#x5B83;&#x4E0D;&#x4F1A;&#x5FAA;&#x73AF;&#x5F15;&#x7528;
};

int main()
{
    std::shared_ptr<a> a(new A());
    std::shared_ptr<b> b(new B());
    a->b_ptr = b;
    b->a_ptr = a;

    std::cout << "A:" << a.use_count() << "\n";
    std::cout << "B:" << b.use_count() << "\n";
}
// * &#x8FD0;&#x884C;&#x7ED3;&#x679C;&#xFF1A;
// A:2
// B:2</b></a></b></b></a></a></memory></iostream>

如何选择智能指针

(1)如果程序要使用多个指向同一个对象的指针,应选择shared_ptr。这样的情况包括:

  • 有一个指针数组,并使用一些辅助指针来标示特定的元素,如最大的元素和最小的元素;
  • 两个对象包含都指向第三个对象的指针;
  • STL容器包含指针。很多STL算法都支持复制和赋值操作,这些操作可用于shared_ptr,但不能用于unique_ptr(编译器发出warning)和auto_ptr(行为不确定)。如果你的编译器没有提供shared_ptr,可使用Boost库提供的shared_ptr。

(2)如果程序不需要多个指向同一个对象的指针,则可使用unique_ptr。如果函数使用new分配内存,并返还指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。这样,所有权转让给接受返回值的unique_ptr,而该智能指针将负责调用delete。可将unique_ptr存储到STL容器在那个,只要不调用将一个unique_ptr复制或赋给另一个算法(如sort())。例如,可在程序中使用类似于下面的代码段。

(3) 基于性能考虑:

1、unique_ptr独占对象的所有权,由于没有引用计数,因此性能较好

2、shared_ptr共享对象的所有权,但性能略差

3、weak_ptr配合shared_ptr,解决循环引用的问题

由于性能问题,那么可以粗暴的理解:优先使用unique_ptr。但由于unique_ptr不能进行复制,因此部分场景下不能使用的。

智能指针的错误用法

1、使用智能指针托管的对象,尽量不要在再使用原生指针

很多开发同学(包括我在内)在最开始使用智能指针的时候,对同一个对象会混用智能指针和原生指针,导致程序异常。

void incorrect_smart_pointer1()
{
    A *a= new A();
    std::unique_ptr<a> unique_ptr_a(a);

    // &#x6B64;&#x5904;&#x5C06;&#x5BFC;&#x81F4;&#x5BF9;&#x8C61;&#x7684;&#x4E8C;&#x6B21;&#x91CA;&#x653E;
    delete a;
}</a>

2、不要把一个原生指针交给多个智能指针管理

如果将一个原生指针交个多个智能指针,这些智能指针释放对象时会产生对象的多次销毁

void incorrect_smart_pointer2()
{
    A *a= new A();
    std::unique_ptr<a> unique_ptr_a1(a);
    std::unique_ptr<a> unique_ptr_a2(a);// &#x6B64;&#x5904;&#x5C06;&#x5BFC;&#x81F4;&#x5BF9;&#x8C61;&#x7684;&#x4E8C;&#x6B21;&#x91CA;&#x653E;
}</a></a>

3、尽量不要使用 get()获取原生指针

void incorrect_smart_pointer3()
{
    std::shared_ptr<a> shared_ptr_a1 = std::make_shared<a>();

    A *a= shared_ptr_a1.get();

    std::shared_ptr<a> shared_ptr_a2(a);// &#x6B64;&#x5904;&#x5C06;&#x5BFC;&#x81F4;&#x5BF9;&#x8C61;&#x7684;&#x4E8C;&#x6B21;&#x91CA;&#x653E;

    delete a;// &#x6B64;&#x5904;&#x4E5F;&#x5C06;&#x5BFC;&#x81F4;&#x5BF9;&#x8C61;&#x7684;&#x4E8C;&#x6B21;&#x91CA;&#x653E;
}</a></a></a>

4、不要将 this 指针直接托管智能指针

class E
{
    void use_this()
    {
        //&#x9519;&#x8BEF;&#x65B9;&#x5F0F;&#xFF0C;&#x7528;this&#x6307;&#x9488;&#x91CD;&#x65B0;&#x6784;&#x9020;shared_ptr&#xFF0C;&#x5C06;&#x5BFC;&#x81F4;&#x4E8C;&#x6B21;&#x91CA;&#x653E;&#x5F53;&#x524D;&#x5BF9;&#x8C61;
        std::shared_ptr<e> this_shared_ptr1(this);
    }
};

std::shared_ptr<e> e = std::make_shared<e>();</e></e></e>

5、智能指针只能管理堆对象,不能管理栈上对象

栈上对象本身在出栈时就会被自动销毁,如果将其指针交给智能指针,会造成对象的二次销毁

void incorrect_smart_pointer5()
{
    int int_num = 3;
    std::unique_ptr<int> int_unique_ptr(&int_num);
}</int>

缺点和优化

  1. 内存占用高
    shared_ptr 的内存占用是裸指针的两倍。因为除了要管理一个裸指针外,还要维护一个引用计数。
    因此相比于 unique_ptr, shared_ptr 的内存占用更高。 性能要求高时,可以用裸指针。
  2. 原子操作性能低
    考虑到线程安全问题,引用计数的增减必须是原子操作。而原子操作一般情况下都比非原子操作慢。可以引入锁机制,或者用裸指针。
  3. 使用移动优化性能
    shared_ptr 在性能上固然是低于 unique_ptr。而通常情况,我们也可以尽量避免 shared_ptr 复制。
    如果,一个 shared_ptr 需要将所有权共享给另外一个新的 shared_ptr,而我们确定在之后的代码中都不再使用这个 shared_ptr,那么这是一个非常鲜明的移动语义。
    对于此种场景,我们尽量使用 std::move,将 shared_ptr 转移给新的对象。因为移动不用增加引用计数,性能比复制更好。

汇总

智能指针能更安全的回收内存,它能防止:

  1. 忘记delete造成的内存泄露

  2. delete了,又被访问到了,比如并发时,导致”野指针”的危险情况

  3. delete了,又被delete了,导致重复回收,导致报错中断程序

总的来说,一般推荐用智能指针,性能要求很高时,可以用裸指针,但要十分小心。

参考

https://docs.microsoft.com/zh-cn/cpp/cpp/smart-pointers-modern-cpp?view=msvc-170

https://www.zhihu.com/question/319277442/answer/2384378560

https://www.cyhone.com/articles/right-way-to-use-cpp-smart-pointer/

https://juejin.cn/post/6844904198962675719

Original: https://www.cnblogs.com/sunsky303/p/16497592.html
Author: sunsky303
Title: 彻底搞懂之C++智能指针

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536295/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • C++map值排序

    class Solution { public: static bool cmp(pair a, pair b){ return a.second>b.second; } s…

    C++ 2023年5月29日
    039
  • c++ 头文件相互包含导致编译问题

    根本原因是用到某个符号的时候符号还没声明,找不到符号导致编译报错 方法是make .. verbose=1,展示所有预处理,编译等详细过程 然后使用 gcc -E ,查看文件包含展…

    C++ 2023年5月29日
    041
  • c++温故之结构体写法

    结构体简介结构体属于聚合数据类型的一类,它将不同的数据类型整合在一起构成一个新的类型,相当于数据库中一条记录,比如学生结构体,整合了学号,姓名等等信息。结构体的好处就是可以对这些信…

    C++ 2023年5月29日
    091
  • JNI NDK (AndroidStudio+CMake )实现C C++调用Java代码流程

    JNI/NDK Java调用C/C++前言通过第三篇文章讲解在实际的开发过程中Java层调用C/C++层的处理流程。其实我们在很大的业务里也需要C/C+ +层去调用Java层,这两…

    C++ 2023年5月29日
    044
  • 2022年第十三届蓝桥杯C++B组国赛思路以及部分代码

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    C++ 2023年5月29日
    046
  • error: Microsoft Visual C++ 14.0 is required问题最佳解决方法

    对于程序员来说,经常pip安装自己所需要的包,大部分的包基本都能安装,但是总会遇到包安装不了的问题,预研学习的动力第一步就被安装包给扼杀了。其中最受困扰的就是这个问题:error:…

    C++ 2023年5月29日
    053
  • C++类中静态数据成员MAP如何初始化

    cpp;gutter:true; conv_xxx.hpp</p> <p>class convolution { …</p> <pre…

    C++ 2023年5月29日
    055
  • c++11 auto 与 decltype 详解

    一. auto简介 编程时候常常需要把表达式的值付给变量,需要在声明变量的时候清楚的知道变量是什么类型。然而做到这一点并非那么容易(特别是模板中),有时候根本做不到。为了解决这个问…

    C++ 2023年5月29日
    061
  • vs2019恢复为c++的布局

    工具 -》导入导出设置 -》重置所有设置 -》备份当前设置后下一步 -》 选择一个默认设置集合 Original: https://www.cnblogs.com/bigben01…

    C++ 2023年5月29日
    061
  • c++类大四个默认函数-构造函数 析构函数 拷贝构造函数 赋值构造函数

    每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺…

    C++ 2023年5月29日
    060
  • 【C++】第1章 在VS2015中用C++编写控制台应用程序

    分类:C++、VS2015 创建日期:2016-06-12 看到不少人至今还在用VC 6.0开发工具学习C++,其实VC 6.0开发工具早就被淘汰了。这里仅介绍学习C++时推荐使用…

    C++ 2023年5月29日
    067
  • Prim算法(二)之 C++详解

    普里姆(Prim)算法,是用来求加权连通图的最小生成树的算法。 基本思想对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放…

    C++ 2023年5月29日
    053
  • C++调用C#的动态库dll

    以往我们经常是需要使用C#来调用C++的dll,这通过PInvoke就能实现。现在在实际的项目过程中,有时会遇到在C++的项目中调用某个C#的dll来完成特定的某个功能,我们都知道…

    C++ 2023年5月29日
    086
  • C++ #ifndef/#define/#endif解释

    作用:防止头文件的重复包含和编译 ifndef x define x endif 比如说有一个头文件叫head.h,这是一个通用的头文件,然后我又定义了两个自己用的头文件,分别叫l…

    C++ 2023年5月29日
    041
  • C++中的静态绑定和动态绑定(转)

    C++在面向对象编程中,存在着静态绑定和动态绑定的定义,本节即是主要讲述这两点区分。我是在一个类的继承体系中分析的,因此下面所说的对象一般就是指一个类的实例。首先我们需要明确几个名…

    C++ 2023年5月29日
    043
  • c++以代理的方式来实现接口化编程

    假如你项目代码本身已经实现了很多的实体类,但并未采用接口,可以考虑以这种方式来实现接口化编程 struct ITest { virtual void Test()=0; }; cl…

    C++ 2023年5月29日
    068
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球