欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++動態(tài)內(nèi)存分配超詳細(xì)講解

 更新時間:2022年08月22日 09:39:42   作者:Shawn-Summer  
給數(shù)組分配多大的空間?你是否和初學(xué)C時的我一樣,有過這樣的疑問。這一期就來聊一聊動態(tài)內(nèi)存的分配,讀完這篇文章,你可能對內(nèi)存的分配有一個更好的理解

1.在類中使用動態(tài)內(nèi)存分配的注意事項

1.1 構(gòu)造函數(shù)中使用new

  • 如果在構(gòu)造函數(shù)中使用new來初始化指針成員,則應(yīng)在析構(gòu)函數(shù)中使用delete
  • newdelete必須相互兼容,new相對delete;new[]相對delete[]
  • 因?yàn)橹挥幸粋€析構(gòu)函數(shù),所有的構(gòu)造函數(shù)都必須與它兼容

注意的是:delete或者delete[]都可以對空指針操作.

NULl0nullptr:空指針可以用0或者NULL來表示,C++11使用一個特殊的關(guān)鍵詞:nullptr來表示空指針.

應(yīng)該定義一個復(fù)制構(gòu)造函數(shù),通過深度復(fù)制將一個對象初始化成另一個對象.

String::String(const String &st)//復(fù)制構(gòu)造函數(shù)
{
    len=st.len;
    str=new char[len+1];
    std::strcpy(str,st.str);
    num_strings++;
}

應(yīng)該定義一個賦值運(yùn)算符。

String& String::operator=(const String& st)//賦值運(yùn)算符
{
    if(this==&st)
    return *this;
    delete[] str;
    len=st.len;
    str=new char[len+1];
    std::strcpy(str,st.str);
    return *this;
}

具體來說,操作是:檢查自我賦值情況,釋放成員指針以前指向的內(nèi)存,復(fù)制數(shù)據(jù)而不僅僅是地址,返回一個指向調(diào)用對象的引用.

一個典型錯誤

String::String()
{
    str="default string";
    len=std::strlen(str);
}

上面這段代碼定義了默認(rèn)構(gòu)造函數(shù),但是它犯了一個錯誤:無法和析構(gòu)函數(shù)中的delete[]匹配.

包含類成員的類的逐成員復(fù)制

class Magazine
{
    private:
        String title;
        String publisher;
}

類成員的類型是String,這是否意味著要為Magazine類編寫復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符?不.

如果你將一個Magazine對象復(fù)制或者賦值給另一個Magazine對象,逐成員復(fù)制將使用成員類型定義的復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符.也就是說復(fù)制title時,將調(diào)用String的復(fù)制構(gòu)造函數(shù),而將title賦值給另一個Magazine對象時,也會使用String的賦值運(yùn)算符.

1.2 有關(guān)返回對象的說明

返回指向const對象的引用

返回對象會調(diào)用復(fù)制構(gòu)造函數(shù)生成臨時對象,而返回const對象的引用不會.

引用指向的對象不能是局部變量. 總之,返回指向const對象的引用,就是按值傳遞的升級版,但是它不能返回局部變量.

返回指向非const對象的引用

例如我們重載<<時,

ostream& operator<<(ostream & os,class_name object); 返回指向非const對象的引用,主要是我們希望對函數(shù)返回對象進(jìn)行修改.

返回對象

就是按值傳遞.

如果我們返回的對象是局部變量,那么我們不能使用引用來返回了,只能采用返回對象.

返回const對象

不太常用.防止用戶對臨時對象進(jìn)行賦值操作,而編譯器不會對這種操作報錯.

總之,如果要返回局部對象就必須返回對象;如果,那必須返回對象的引用;如果返回對象也行,返回指向?qū)ο蟮囊靡残?那優(yōu)先使用引用版本,因?yàn)樾矢?

1.3 使用new創(chuàng)建對象

String * glop=new String("my my my");

這句話會使用構(gòu)造函數(shù)String(const char *);

glop->類成員

可以使用這種方式調(diào)用對象成員,學(xué)過C語言的應(yīng)該明白。

對于動態(tài)分配的對象,它的析構(gòu)函數(shù)當(dāng)且僅當(dāng)使用delete刪除對象時,它的析構(gòu)函數(shù)才會調(diào)用。

定位new的用法

#include<iostream>
#include<string>
#include<new>
using std::string;
using std::cout;
using std::cin;
using std::endl;
const int BUF=512;
class JustTesting
{
    private:
        string words;
        int number;
    public:
        JustTesting(const string & s="Just Testing",int n=0)
            :words(s),number(n){cout<<words<<" constructed.\n";}
        ~JustTesting(){cout<<words<<" destoryed!\n";}
        void show() const {cout<<words<<", "<<number<<endl;}
};
int main()
{
    char * buffer=new char[BUF];//獲得一塊512B內(nèi)存
    JustTesting *pc1,*pc2;
    pc1=new(buffer) JustTesting;//在該塊內(nèi)存中分配空間
    pc2=new JustTesting ("Heap1",20);
    cout<<"Memory block addresses:\n"<<"buffer: "<<(void*)buffer<<" heap: "<<pc2<<endl;
    cout<<"Memory contents:\n";
    cout<<pc1<<": ";
    pc1->show();
    cout<<pc2<<": ";
    pc2->show();
    JustTesting *pc3,*pc4;
    pc3=new(buffer+sizeof(JustTesting)) JustTesting ("Bad Idea",6);
    pc4=new JustTesting ("Heap2",10);
    cout<<"Memory contents:\n";
    cout<<pc3<<": ";
    pc3->show();
    cout<<pc4<<": ";
    pc4->show();
    delete pc2;
    delete pc4;
    pc3->~JustTesting();
    pc1->~JustTesting();
    delete [] buffer;
    cout<<"done !\n";
}

Just Testing constructed.
Heap1 constructed.
Memory block addresses:
buffer: 0xf040a0 heap: 0xf042d0
Memory contents:
0xf040a0: Just Testing, 0
0xf042d0: Heap1, 20
Bad Idea constructed.
Heap2 constructed.
Memory contents:
0xf040c8: Bad Idea, 6
0xf04330: Heap2, 10
Heap1 destoryed!
Heap2 destoryed!
Bad Idea destoryed!
Just Testing destoryed!
done !

上面這段代碼演示了定位new的用法,這個我們之前在內(nèi)存模型中談過。這里需要注意的是,如果使用定位new創(chuàng)建對象,如何確保其析構(gòu)函數(shù)被調(diào)用,我們不能使用delete p3;delete p1;,這是因?yàn)?code>delete和定位new不匹配,我們必須顯式調(diào)用析構(gòu)函數(shù)p1->~JustTesting();。

2.隊列模擬

和棧(Stack)一樣,隊列(Queue)也是一個很重要的抽象數(shù)據(jù)結(jié)構(gòu)。這一節(jié)將會構(gòu)建一個Queue類,順便復(fù)習(xí)之前所學(xué)的技術(shù)和學(xué)習(xí)少量新知識。

我們采用鏈表來實(shí)現(xiàn)隊列。

2.1 類聲明中的一些思考

typedef  std::string Item;
class Queue
{
    private:
        struct Node
        {
            Item item;
            struct Node *next;
        };
        enum{Q_SIZE=10};
        Node* front;//隊首指針
        Node* rear;//隊尾指針
        int items;//隊列中的元素個數(shù)
        const int qsize;//隊列的最大元素個數(shù)
        //搶占式定義
        Queue(const Queue & q):qsize(0){}
        Queue & operator=(const Queue & q){return *this;}
    public:
        Queue(int qs=Q_SIZE);
        ~Queue();
        bool isempty() const;//空
        bool isfull() const;//滿
        int queuecount() const;//隊列中元素個數(shù)
        bool enqueue(const Item &i);//入隊
        bool dequeue(Item & i);//出隊
        void show() const;        
};

類作用域中的結(jié)構(gòu)體

類似于類作用域中的常量,通過將結(jié)構(gòu)體Node聲明放在Queue類的私有部分,就可以在類作用域中使用該結(jié)構(gòu)體。這樣就不用擔(dān)心,Node聲明和某些全局聲明發(fā)生沖突。此外,類聲明中還能使用Typedef或者namespace等聲明,都可以使其作用域變成類中。

利用構(gòu)造函數(shù)初始化const數(shù)據(jù)成員

在類中qsize是隊列最大元素個數(shù),它是個常量數(shù)據(jù)成員

Queue::Queue(int qs)
{
    qsize=qs;
    front =rear=nullptr;
    items=0;
}

上面這段代碼是錯誤的。因?yàn)槌A渴遣辉试S被賦值的。C++提供了一種新的方式來解決這一問題–成員初始化列表。

成員初始化列表語法

它的作用是,在調(diào)用構(gòu)造函數(shù)的時候,能夠初始化數(shù)據(jù)。對于const類成員,引用數(shù)據(jù)成員,都應(yīng)該使用這種語法。

于是,構(gòu)造函數(shù)可以這樣:

Queue::Queue(int qs):qsize(qs)
{
    front =rear=nullptr;
    items=0;
}

而且這種方法不限于初始化常量,還能初始化非const變量。則構(gòu)造函數(shù)也可以這樣:

Queue::Queue(int qs):qsize(qs),front(nullptr),rear(nullptr),items(0){}

但是,成員初始化列表語法只能用于構(gòu)造函數(shù)。

類內(nèi)初始化

在C++中,其實(shí)還有一種更直觀的初始化方式,那就是直接在類聲明中進(jìn)行初始化。

class Classy
{
  int mem1=10;
  const int mem2=20;
};

相當(dāng)于在構(gòu)造函數(shù)中使用

Classy::Classy():mem1(10),mem2(20){...}

但是如果你同時使用類內(nèi)初始化和成員列表語法時,調(diào)用相應(yīng)構(gòu)造函數(shù)時,成員列表語法會覆蓋類內(nèi)初始化。

Classy::Classy(int n):mem1(n){...}

調(diào)用上面這個構(gòu)造函數(shù)時,mem1會被設(shè)置成n,而mem2由于類內(nèi)初始化的原因被設(shè)置成20.

是否需要顯式析構(gòu)函數(shù)?

Queue類的構(gòu)造函數(shù)中是不需要使用new的,因?yàn)闃?gòu)造函數(shù)只是構(gòu)造一個空隊列,那這是不是意味著不需要在析構(gòu)函數(shù)中使用delete?

我們知道,雖然構(gòu)造函數(shù)不需要new,但是在enqueue入隊時,我們需要new一個新元素加入隊列。那么我們必須在析構(gòu)函數(shù)中使用delete以確保所有動態(tài)分配的空間被釋放。

偽私有方法(搶占式定義)

既然我們在Queue類中,使用了動態(tài)內(nèi)存分配,那么編譯器提供的默認(rèn)復(fù)制構(gòu)造函數(shù),和默認(rèn)賦值運(yùn)算符是不正確的。我們假設(shè)隊列是不允許被賦值或者復(fù)制的,那么我們可以使用偽私有方法,目的是禁用某些默認(rèn)接口。

class Queue
{
  private:
    Queue(const Queue & q):qsize(0){}
    Queue & operator=(const Queue & q){return *this;}
}

這樣做的原理是:在私有部分搶先定義了復(fù)制構(gòu)造函數(shù),賦值運(yùn)算符,那么編譯器就不會提供默認(rèn)方法了,那么對象就無法調(diào)用這些方法。

C++提供了另一種禁用方法的方式–使用關(guān)鍵詞delete

class Queue
{
  public:
    Queue(const Queue & q)=delete;
    Queue & operator=(const Queue & q)=delete;
}

可以直接在公有部分中禁用某種方法。

2.2 代碼實(shí)現(xiàn)

//queue.h
#ifndef QUEUE_H_
#define QUEUE_H_
#include<string>
typedef  std::string Item;
class Queue
{
    private:
        struct Node
        {
            Item item;
            struct Node *next;
        };
        enum{Q_SIZE=10};
        Node* front;//隊首指針
        Node* rear;//隊尾指針
        int items;//隊列中的元素個數(shù)
        const int qsize;//隊列的最大元素個數(shù)
        //搶占式定義
        Queue(const Queue & q):qsize(0){}
        Queue & operator=(const Queue & q){return *this;}
    public:
        Queue(int qs=Q_SIZE);
        ~Queue();
        bool isempty() const;//空
        bool isfull() const;//滿
        int queuecount() const;//隊列中元素個數(shù)
        bool enqueue(const Item &i);//入隊
        bool dequeue(Item & i);//出隊
        void show() const;        
};
#endif
//queue.cpp
#include"queue.h"
#include<iostream>
Queue::Queue(int qs):qsize(qs)
{
    front =rear=nullptr;
    items=0;
}
Queue::~Queue()
{
    Node * p;
    while (front!=nullptr)
    {
        p=front;
        front=front->next;
        delete p;
    }
}
bool Queue::isempty() const
{
    return items==0;
}
bool Queue::isfull() const
{
    return items==qsize;
}
int Queue::queuecount() const
{
    return items;
}
bool Queue::enqueue(const Item &i)
{
    if(isfull())
        return false;
    Node *add=new Node;
    add->item=i;
    add->next=nullptr;
    items++;
    if(front==nullptr)//隊空
        front=rear=add;
    else
    {
        rear->next=add;
        rear=add;
    }
    return true;
}
bool Queue::dequeue(Item & i)
{
    if(isempty())
        return false;
    i=front->item;
    items--;
    if(items==0)
    {
        delete front;
        front=rear=nullptr;
    }
    else
    {
        Node *p=front;
        front=front->next;
        delete p;
    }
    return true;
} 
void Queue::show() const
{
    using std::cout;
    using std::endl;
    cout<<"the items: "<<items<<endl;
    if(isempty())
        cout<<"Empty queue!\n";
    else
    {
        cout<<"front: ";
        for(Node*p=front;p!=nullptr;p=p->next)
        {
            cout<<p->item;
            if(p!=rear)
                cout<<"-> ";
        }
        cout<<" :rear\n";
    }
}
//queuetest.cpp
#include"queue.h"
#include<iostream>
int main()
{
    using std::cin;
    using std::cout;
    using std::endl;
    using std::string;
    Queue test(8);
    char choice;
    cout<<"Enter E to enqueue ,D to dequeue,Q to quit: ";
    while(cin>>choice)
    {
        string temp;
        switch (choice)
        {
        case 'E':
            cout<<"Enter the string: ";
            cin>>temp;
            if(test.enqueue(temp))
                test.show();
            else
                cout<<"can't enqueue\n";
            break;
        case 'D':
            if (test.dequeue(temp))
            {
                cout<<"the item gotten: "<<temp<<endl;
                test.show();
            }
            else
                cout<<"can't dequeue\n";
            break;
        case 'Q':
            goto aa;
            break;
        default:
            break;
        }
        cout<<endl;
        cout<<"Enter E to enqueue ,D to dequeue,Q to quit: ";
        cin.ignore();
    }
    aa:test.~Queue();
    cout<<"Bye!: ";
    test.show();
}

PS D:\study\c++\path_to_c++> .\queue.exe
Enter E to enqueue ,D to dequeue,Q to quit: E
Enter the string: apple
the items: 1
front: apple :rear

Enter E to enqueue ,D to dequeue,Q to quit: E
Enter the string: banana
the items: 2
front: apple-> banana :rear

Enter E to enqueue ,D to dequeue,Q to quit: E
Enter the string: candy
the items: 3
front: apple-> banana-> candy :rear

Enter E to enqueue ,D to dequeue,Q to quit: E
Enter the string: dizzy
the items: 4
front: apple-> banana-> candy-> dizzy :rear

Enter E to enqueue ,D to dequeue,Q to quit: D
the item gotten: apple
the items: 3
front: banana-> candy-> dizzy :rear

Enter E to enqueue ,D to dequeue,Q to quit: D
the item gotten: banana
the items: 2
front: candy-> dizzy :rear

Enter E to enqueue ,D to dequeue,Q to quit: Q
Bye!: the items: 2
front:  :rear

到此這篇關(guān)于C++動態(tài)內(nèi)存分配超詳細(xì)講解的文章就介紹到這了,更多相關(guān)C++動態(tài)內(nèi)存分配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • linux使用gcc編譯c語言共享庫步驟

    linux使用gcc編譯c語言共享庫步驟

    這篇文章主要介紹了在linux中使用gcc編譯c語言共享庫的步驟,大家參考使用吧
    2014-01-01
  • VS2019配置opencv詳細(xì)圖文教程和測試代碼的實(shí)現(xiàn)

    VS2019配置opencv詳細(xì)圖文教程和測試代碼的實(shí)現(xiàn)

    這篇文章主要介紹了VS2019配置opencv詳細(xì)圖文教程和測試代碼的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • opencv提取水平與垂直線條

    opencv提取水平與垂直線條

    這篇文章主要為大家詳細(xì)介紹了opencv提取水平與垂直線條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • c++ Qt信號槽原理

    c++ Qt信號槽原理

    這篇文章主要介紹了c++ Qt信號槽原理的相關(guān)資料,幫助大家更好的理解和使用c++,感興趣的朋友可以了解下
    2021-02-02
  • C語言編寫猜數(shù)游戲

    C語言編寫猜數(shù)游戲

    這篇文章主要為大家詳細(xì)介紹了C語言編寫猜數(shù)游戲,可以自定義猜數(shù)范圍和機(jī)會次數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C語言文件操作零基礎(chǔ)新手入門保姆級教程

    C語言文件操作零基礎(chǔ)新手入門保姆級教程

    在實(shí)際應(yīng)用中,我們往往需要對文件進(jìn)行操作,下面這篇文章主要給大家分享了關(guān)于C語言文件操作的零基礎(chǔ)新手入門保姆級教程,文中通過示例代碼以及圖片介紹的非常詳細(xì),需要的朋友可以參考下
    2021-10-10
  • C++?OpenCV裁剪圖片時發(fā)生報錯的解決方式

    C++?OpenCV裁剪圖片時發(fā)生報錯的解決方式

    在圖像處理中,我們經(jīng)常根據(jù)需要截取圖像中某一區(qū)域做處理,下面這篇文章主要給大家介紹了關(guān)于C++?OpenCV裁剪圖片時發(fā)生報錯的解決方式,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • Qt讀寫XML文件的方法詳解(含源碼+注釋)

    Qt讀寫XML文件的方法詳解(含源碼+注釋)

    XML文件可以用來存儲項目中的數(shù)據(jù),它相當(dāng)于一個簡單的數(shù)據(jù)庫,下面這篇文章主要給大家介紹了關(guān)于Qt讀寫XML文件(含源碼+注釋)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • Ubuntu18.04下QT開發(fā)Android無法連接設(shè)備問題解決實(shí)現(xiàn)

    Ubuntu18.04下QT開發(fā)Android無法連接設(shè)備問題解決實(shí)現(xiàn)

    本文主要介紹了Ubuntu18.04下QT開發(fā)Android無法連接設(shè)備問題解決實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • php5系列的apache遠(yuǎn)程執(zhí)行漏洞攻擊腳本

    php5系列的apache遠(yuǎn)程執(zhí)行漏洞攻擊腳本

    這篇文章主要介紹了php5系列的apache遠(yuǎn)程執(zhí)行漏洞攻擊腳本,需要的朋友可以參考下
    2014-06-06

最新評論