什么樣的類不適宜用智能指針(Boolan)

我最不喜歡循規(guī)循矩,雖然是讓寫筆記,照著老師的ppt抄一遍有什么意思。所以我還是喜歡寫自己的東西。

最近我有個(gè)怪癖,愛把所有帶指針的類都改造成使用智能指針來控制資源分配和回收。因?yàn)槲艺J(rèn)為既然是c++11標(biāo)準(zhǔn)出的應(yīng)該是可以頂替99標(biāo)準(zhǔn),更安全更先進(jìn)的用法,為什么不用呢?結(jié)果在這兩周侯捷老師的c++課上的例子的智能指針改寫上吃了苦頭,也領(lǐng)悟到什么時(shí)候該用智能指針,什么時(shí)候不該用。離提交作業(yè)的日期只剩兩天不到,有空的話我會(huì)將我對(duì)Date類,Rectangle類的改寫也在這里講一下。

我這里目的并不是講解智能指針,所以講的并不細(xì)。

什么是智能指針:

智能指針就是通過模板實(shí)現(xiàn)的對(duì)普通指針的一種封裝,我偏愛用shared_ptr指針。通過引用計(jì)數(shù),來管理自己的引用數(shù)量,當(dāng)計(jì)數(shù)為0時(shí),自動(dòng)釋放內(nèi)存。

什么時(shí)候該用它呢?

本來我有個(gè)A類型的指針:

A *a = new A();

這樣我還要操心去delete它。假如這行代碼在一個(gè)函數(shù)內(nèi),并且a會(huì)作為該函數(shù)的返回值返回的話(比如第一周的作業(yè)Date類隨機(jī)產(chǎn)生10個(gè)日期返回的函數(shù))那選擇什么時(shí)候delete就很重要了。有的時(shí)候不好抉擇,增加維護(hù)的復(fù)雜程度。

那么這就是智能指針大顯身手的時(shí)候。

shared_ptr<A> a = make_shared<A>();

起到了和剛才那條語句一樣的效果。
假如這個(gè)函數(shù)是

shared_ptr<A> fun() {
  shared_ptr<A> a = make_shared<A>();
// 對(duì)a做點(diǎn)什么
 return a;
}

這樣的話,函數(shù)里定義a,引用計(jì)數(shù)為1,返回a,a的引用計(jì)數(shù)仍為1.而當(dāng)a使用完畢后,到了函數(shù)使用時(shí)所在作用域的結(jié)束位置時(shí)。a的引用計(jì)數(shù)會(huì)-1,此時(shí)為0,自動(dòng)釋放內(nèi)存。不用我們?nèi)斯elete了。

String類不使用shared的模樣

這個(gè)String類是我自己寫的,但跟課程里的只有一點(diǎn)點(diǎn)不一樣。
返回成員的函數(shù)返回類型我設(shè)為const char*,因?yàn)檫@樣的話可以避免使用函數(shù)時(shí)對(duì)成員做出我們不期望的修改。


#ifndef STRING_H
#define STRING_H

#include <cstring>
#include <iostream>
using std::ostream;

class String {
public:
    String(const char *cstr = 0);
    String(const String&);
    String &operator=(const String&);
    ~String();
    const char* get() const;
private:
    char *m_data;
};

inline
String::String(const char *cstr) {
    if (cstr) {
        m_data = new char[strlen(cstr) + 1];
        strcpy(m_data, cstr);
    } else {
        m_data = new char[1];
        *m_data = '\0';
    }
}

inline
String::String(const String &str) {
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
}

inline
String& String::operator=(const String &right) {
    if (this == &right) return *this;

    delete[] m_data;
    m_data = new char[strlen(right.m_data) + 1];
    strcpy(m_data, right.m_data);

    return *this;
}

inline
String::~String() {
    delete[] m_data;
}

inline
const char* String::get() const {  // 這里返回類型加了const
    return m_data;
}

inline
ostream& operator<<(ostream& os, const String& str) {
    // auto s = str.get();
    // strcpy(s, "h");
    return os << str.get();
}
#endif

這是使用后(慘不忍睹,跑不起來):

#ifndef STRING_H
#define STRING_H

#include <cstring>
#include <memory>
#include <iostream>
using std::ostream;
using std::shared_ptr;
using std::make_shared;

class String {
public:
    String(const char *cstr = 0);
    String(const String&);
    String &operator=(const String&);
    ~String();
    const char* get() const;
private:
    shared_ptr<char> m_data;
};

inline
String::String(const char *cstr) {
    char* pstr; 
    if (cstr) {
        pstr = new char[strlen(cstr) + 1];
        strcpy(pstr, cstr);
    } else {
        pstr = new char[1];
        pstr = '\0';
    }
    m_data = make_shared<char> (pstr, [](char *pstr){ delete[] pstr; });
}

inline
const char* String::get() const {
    return m_data.get();
}

inline
ostream& operator<<(ostream& os, const String& str) {
    return os << str.get();
}
#endif

這里可以明確的告訴大家,這段程序跑不起啦。但是這里可以看出智能指針的一個(gè)優(yōu)點(diǎn)。那就是使用智能指針后,所有成員都不是指針類型,若沒有特殊操作的話,拷貝構(gòu)造,賦值運(yùn)算符重載,析構(gòu),三大函數(shù)都可以只用默認(rèn)的就可以,極大的減少了代碼量。

但這個(gè)類并不適宜用智能指針。原因現(xiàn)在可能看不太出來。接下來我會(huì)將我對(duì)Date類的改寫和Rectangle類的改寫放出來。這樣就比較清晰了。

Date類同理,也跑不起來,但我還是在這里把代碼貼上

/*
 * 用于隨機(jī)生成和排序的數(shù)組
 */
const int DATE_SIZE = 10;
/*
 * 生成10個(gè)隨機(jī)日期
 */
unique_ptr<Date[]> CreatePoints() {
    unique_ptr<Date[]> dates(new Date[DATE_SIZE]);
    for (int i = 0; i < DATE_SIZE; ++i) {
        int year = rand() % 1000 + 1500;
        int month = rand() % 12 + 1;
 
        int maxDay = 28;
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                maxDay = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                maxDay = 30;
                break;
            case 2:
                if ((year % 4 == 0 && year % 100 != 0)
                        || year % 400 == 0) 
                    maxDay = 29;
                break;
        }
        int day = rand() % maxDay + 1;
 
        dates[i] = Date(year, month, day);
    }
 
    return dates;
}
 
/*
 *    按從小到大的順序排列日期數(shù)組
 */
unique_ptr<Date[]> Sort(unique_ptr<Date[]> &dates) {
    sort(&dates[0], &(dates[DATE_SIZE - 1]) + 1);
    return dates;
}

這兩者的共同點(diǎn)就是需要改造成智能指針的都是數(shù)組,也就是說,如果數(shù)組之類對(duì)要綁定成智能指針的變量有依賴或順序關(guān)系的情形下,使用智能指針并不明智,c++ primer書中也講,盡量少用數(shù)組,多用標(biāo)準(zhǔn)庫容器。而不得不用數(shù)組的情況下,還是使用new,delete管理或別的方式比較好。

而有的情形很適合智能指針,比如下面的Rectangle類:

class Shape {
    int no;
};

class Point {
    int x, y;
public:
    Point() = default;
    Point(const int &x = 0, const int &y = 0):x(x), y(y){}
};

class Rectangle: public Shape {
    int width, height;
    shared_ptr<Point> leftUp;
public:
    Rectangle(const int&, const int&, const int&, const int&);
    // 使用智能指針,拷貝,賦值,析構(gòu)使用默認(rèn)即可
};

inline
Rectangle::Rectangle(const int &w, const int &h, const int &x, const int &y):leftUp(make_shared<Point>(x, y)), width(w), height(h){}

這樣成員只是單個(gè)對(duì)象的時(shí)候?qū)⑵涓臑橹悄苤羔樉秃芩?,Rectangle類的拷貝,賦值,析構(gòu)全部省去,也不用有釋放內(nèi)存問題的擔(dān)心。

這只是我這幾天學(xué)習(xí)c++以來的想法,不一定正確,等以后功力深了說不定我自己都會(huì)推翻現(xiàn)在的想法。所以有什么疑問盡管在下面留言,希望我們的思想能碰撞出智慧的火光,共同進(jìn)步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容