• c++ 基础知识回顾


    1.数据类型和类。

    所有编程都是处理输入和输出。

    关于输入参数,从右至左把参数入栈,这样根据栈基址,可以定位第一个参数。因为很多函数是参数数量不定的,比如printf.

    关于输出,记得输出时,一般是把值放入eax 寄存器,

      所以一般数据类型,直接放入返回值数据,寄存器可以装下,

      而返回对象,会返回对象的指针,并在调用者的栈中,按位复制。之后清栈

      而返回指针,会把指针放入寄存器,之后清栈。

    所以第一条守则:不要返回或传参为函数局部对象的指针,而是返回局部对象或堆中地址。

    隐藏守则:如果是返回一个集合,如vector<>,而里面是指针,就相当于返回了局部对象的指针。

    所以最佳实践:

    函数中:

    对象的创建,必须明确,仅仅是局部使用,还是会传出去,局部使用,就可以直接在栈中,如果会传出去(或返回,或包含在返回对象中,或作为参数),必须传递对象,或者堆中的地址(智能指针)

    就算是主函数中,不会清栈的情况下。还是为了语义的正确和统一性已经性能,也必须在堆中申请。

    类中:

    如果是类中的变量,自己new 了,必须自己delete .如果是指针,由外部传入,那么必须使用智能指针(因为你不确定使用者何时会delete)

    一句话,除考虑性能之外,判断是否new,看变量的生存期是本函数,还是超过本函数。

    考虑性能,可以对于生存期在本函数类的变量。new 之后再 delete.极端情况是个大数据,而中间还要调用很深的栈。

    非常明确的局部使用小对象,小数据,不需要new.否则智能指针吧。

    当然根据 raii,用类管理资源的守则,应该写成用类管理资源。

    但是默认的复制构造函数是按位复制。所以会出现2次析构。导致错误。

    所以1,自定义复制构造函数。

     2,使用智能指针。

    使用智能指针,就为我们定义了一个根据引数进行的析构函数,只有当引数为0才进行delete 操作。

    使用智能指针,就要说到为什么使用指针。必须要使用指针的场合,自己好像只碰到过的,只有在继承场合下。而且此指针需要传递给外部。否则可以局部对象,加&。

    所以准则2:当由于继承或其他情况下需要使用指针时,使用智能指针代替原指针。用智能指针这个模板类来管理资源。


    再返回去说数据。基本数据没什么好说。int,char,都是定死的资源。对于c来说。使用指针感觉就是为了char *.字符串。

    类,只包含基本类型。也没什么好说。复制是成员数据类型按位复制,

    如果类包含另外一个类的对象。那么1,为了节省栈空间。可以只包含类的指针。2,同一个对象会被不同的类包含,那么必须使用指针就节省了整个内存。

    使用指针,当然就是用智能指针,省去自己处理拷贝构造函数的编写。

    根据准则一,不要返回局部变量的指针,我们可以返回局部变量,而继承必须使用指针来表示。所以有一种必须使用new  的场合,就是返回包含派生类指针的集合。

    所以目前总结,适合使用到智能指针(指针)的场合:1.继承,2。某个类的对象,会被多个另外不同的对象包含。3.需要作为返回值,而为了性能,可以返回堆中的地址(智能指针)4.容器包含指针,而且作为返回值,必须在堆中new.

    所以准则3:

    不考虑性能问题,必须使用new,就1种情况,需要返回派生类指针的集合,如 vector<base *> fun (); 那么必须在函数中用new;

    考虑性能,使用new,多3种情况。1.返回智能指针,而不是类对象。2.某个类的对象,会被另外类的多个不同的对象包含,那么复合类,采用智能指针,3某个类的成员是个大数据,比如缓存类,必须使用new到堆中,栈放不下。以免使用者,直接

    在栈中生成对象。

    而使用指针在上面的情况外。就是继承。

    2.再谈输入与输出。

    所以综合考虑,写函数。除非性能和栈空间的考虑,基本可以不用到new .除非需要返回派生类指针的集合

    而使用指针也很有限。1.就是为了多态,使用指针。2.传递指针给函数,函数需要修改数据。

    3构造和析构

    如上所诉,类中需要使用智能指针成员,就是1,为了多态。2为了性能。

    感觉不如用function.

    构造函数中不能调用基类的虚构造。

    析构函数不让异常逃出。

     基类必须实现虚析构。

    5.资源管理

    6.一般实践:

    函数内部变量:

    1.非常明确的局部变量(不返回,不作为参数)的小数据,用对象,不需要new.

    2.否则智能指针吧。

    函数参数和返回值:

    1.不需要修改那么const ref

    2.如要修改用原始指针

    3.返回必定智能指针(google 的硬性规定就是指针,好像不推荐返回对象)

    见例子,非常实用的干货。

    作者:陈硕
    链接:https://www.zhihu.com/question/22821303/answer/22759540
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    class Request : boost::noncopyable { /* ... */ };
    class Buffer { /* network I/O buffer */ };
    
    // return unique_ptr means caller taking ownership
    std::unique_ptr<Request> parseRequest(Buffer* inputMessage)
    {
      std::unique_ptr<Request> req(new Request); // make_unique<Request>() in C++14
      // fill in req with inputMessage
      return std::move(req);
    }
    
    // pass unique_ptr means callee taking ownership
    void processRequest(std::unique_ptr<Request> req)
    { /* may process in a different thread, taking ownership of req */ }
    
    // pass raw pointer means changing it but not taking ownership
    void augmentRequest(Request* req)
    { /* fill in more fields, but not keeping the pointer longer than 
         the scope of this function. */ }
    
    // pass const reference means read it but not taking ownership
    bool validateRequest(const Request& req)
    { /* do not keep the pointer or reference outside this function. */ }
    
    void onNetworkMessage(Buffer* inputMessage)
    {
      std::unique_ptr<Request> req = parseRequest(inputMessage);
      augmentRequest(req.get());
      if (validateRequest(*req)) {
        processRequest(std::move(req));
      }
    }

    类内部变量:

    以下3种情况有智能指针,或者引用。否则直接对象。

    1)多态必须指针,就智能指针
    2)可能和别的类共享,或者只是引用某个对象,所以生命周期不是很确定(考虑智能指针)

    就比如lock_gurad ,锁类,里面的互斥锁mutex ,必须是外部对象引用(stl里面放入的是引用)。 智能锁类,不管理锁本身内存,只关注锁的锁定和放锁。

    #include <iostream>
    #include "stdio.h"
    #include <memory>
    #include <unistd.h>
    #include <thread>
    #include <vector>
    #include <mutex>
    #include <unistd.h>
    #include <stdlib.h>
    
    using namespace std;
    
    mutex mtx;
    
    class mylock_guard
    {
    public:
        mylock_guard(mutex& _mtx):mtx(_mtx)
        {
            mtx.lock();
        }
        
        ~mylock_guard()
        {
            mtx.unlock();
        }
        
    private:
        mylock_guard(const mylock_guard&);
        mylock_guard& operator=(const mylock_guard&);
        mutex& mtx;
    };
    
    
    void ShowMsg()
    {
        //lock_guard<mutex> lck(mtx);
        mylock_guard lck(mtx);
        cout<<"1 seconed"<<endl;
        sleep(1);
    }
    
    int main()
    {
        
        thread t1(ShowMsg);
        thread t2(ShowMsg);
        thread t3(ShowMsg);
        
        t1.detach();
        t2.detach();
        t3.detach();
        
        string cmd;
        cin>>cmd;
    }

    3)大数据,如果定义成对象,会导致当前对象无法在栈内创建

  • 相关阅读:
    c#读取Excel并显示出来
    异步编程模型。
    细分线程的等待(WaitAny,WaitAll),区别于Thread.Join
    waitany,waitall在线程池中的使用。from msdn
    SQL SET NOCOUNT ON的含义和作用
    MS ゴシック与MS UI Gothic的区别
    线程的管理1
    HDU 1203 I NEED A OFFER!(背包)
    hdu 1232 畅通工程
    POJ 1088 滑雪
  • 原文地址:https://www.cnblogs.com/lsfv/p/6677947.html
Copyright © 2020-2023  润新知