簡體   English   中英

C ++內存泄漏字符*

[英]C++ Memory Leak char *

所以我有課:

class Worker{
private:
   char *workerName;
   string SSN;
public:
   Worker();
   Worker(char *, string);
   ~Worker();
   void setWorkerName(char *);
   void setSSN();
   char *getWorkerName();
   string getSSN();
   virtual void printValues();
}

所以我在測試函數中創建了一個類:

int main(void){
   Worker Person("Person", "555-55-5555");
   //call print member function
   return 0;
}

(在這里,我也從字符串常量到char *的轉換已被棄用,不知道為什么)

而我的構造函數是:

Worker::Worker(){
   workerName = new char [40];
   SSN = " ";
}

Worker::Worker(char * name, string SSN){
   workerName = new char [40];
   strcpy(workerName, name);
   this->SSN = SSN;
}

Worker::~Worker(){
   delete[] workerName;
}

當我鍵入此內容時,我現在意識到可能沒有第二個構造函數為char *分配內存。 如果那是問題,我將如何處理?

現在,由於您始終在析構函數中執行delete[] workerName ,因此您將在調用Person("Person", "555-55-5555");時嘗試刪除靜態分配的字符串Person("Person", "555-55-5555"); 這沒有意義。

您應該保持一致。 我建議您分配內存,並將輸入字符串復制到第二個構造函數中的workerName中。

3的規則指出,如果您需要析構函數,副本構造函數或副本賦值運算符,則很可能需要全部三個。

例如為了正確處理復制

由於您必須使用char指針(希望可以使用char char const* ?),因此您最好的做法是為此類指針表示的字符串值創建一個包裝器對象,其中包裝器對象負責正確處理邏輯復制。 然后使用該包裝對象而不是原始指針。 通常,這是一種很好的C ++做法,除了對於字符串而言,可以使用字符串。 :-)


修正案 :考慮到您的教授(顯然不太好)所允許的評論,請問他以下條件是否可以接受:

#include <algorithm>    // std::swap
#include <string.h>     // ::strcpy, ::strlen

namespace my {
    auto duplicate( char const* const s )
        -> char const*
    { return ::strcpy( new char[strlen( s ) + 1], s ); }

    class String_value
    {
    private:
        char const*     chars_;

    public:
        auto pointer() const
            -> char const*
        { return chars_; }

        void swap_with( String_value& other ) throw()
        { std::swap( chars_, other.chars_ ); }

        void operator=( String_value other )
        { swap_with( other ); }

        ~String_value()
        { delete[] chars_; }

        String_value( char const* const s )
            : chars_( duplicate( s ) )
        {}

        String_value( String_value const& other )
            : chars_( duplicate( other.chars_ ) )
        {}
    };
}  // namespace my

class Worker
{
private:
    my::String_value    name_;
    my::String_value    ssn_;

public:
    auto name() const
        -> char const*
    { return name_.pointer(); }

    auto ssn() const
        -> char const*
    { return ssn_.pointer(); }

    Worker( char const name[], char const ssn[] )
        : name_( name )
        , ssn_( ssn )
    {}
};

#include <ostream>

std::ostream& operator<<( std::ostream& stream, Worker const& w )
{
    return stream << "Worker(" << w.name() << ", " << w.ssn() << ")";
}

#include <iostream>
auto main()
    -> int
{
    using namespace std;
    Worker const person( "Person", "555-55-5555" );
    cout << person << endl;
}
  1. 您應該只對所有字符串使用std::string 這樣可以解決您的內存問題(至少現在是這樣)。
  2. 您的兩參數構造函數已損壞。 當您要將字符串參數傳遞給函數時,應使用const string&而不是string 后者會創建您不需要的字符串的臨時副本。 由於string具有采用const char*的構造const char* ,因此您也可以傳入C字符串。 (使用const string&&可能會有C ++ 11優化,但我忘了。)
  3. 逐字返回字符串時,還應返回const string& ,以免進行不必要的復制。 返回string的唯一時間是在返回臨時string時(例如,將string類的二進制+操作聲明為string operator +(const string&)因為返回的字符串既不是左手字符串也不是右手字符串,而是一個新的串)。
  4. 通過使用string s進行存儲,您不需要析構函數,因為string在銷毀時會釋放存儲空間。
  5. 您可以在參數列表之后將不將對象更改為const方法標記為。 這使得this成為const Worker* 沒有以這種方式標記為const方法不能在const Worker對象上使用。
  6. 在類中定義簡單的方法。 這使編譯器可以輕松地內聯它們。

然后您的代碼如下所示:

class Worker {
private:
   string workerName;
   string SSN;
public:
   Worker() {}
   Worker(const string& workerName_, const string& SSN_)
   : workerName(workerName_), SSN(SSN_) {}
   void setWorkerName(const string& newName) { workerName = newName; }
   void setSSN(const string& newSSN) { SSN = newSSN; }
   const string& getWorkerName() const { return workerName; }
   const string& getSSN() const { return SSN; }
   virtual void printValues() const;
};

int main(void) {
   Worker Person("Person", "555-55-5555");
   Person.printValues();
   return 0;
}

void Worker::printValues() const {
    cout << "Name: " << workerName << ", SSN: " << SSN << endl;
}

好的,所以您的教授希望您使用char*並手動進行管理。 隨你。 現在,您需要該析構函數,副本構造函數和賦值。 知道什么? 讓我們大開眼界, 防止復制僅僅是因為。

在C ++中,字符串文字是const char*不是 char* ,因此,如果您的教授期望使用char*則會收到編譯器警告。 如果他認為您的const正確錯誤的 ,請與他的老板聯系,因為教授無能並且正在教授會在現實世界中引起問題的做法。

因此,讓我們再次遍歷代碼,將string版本轉換為char*版本。 首先上課。 因為設置字符串的任何內容都是不平凡的,所以大多數內聯方法不再內聯:

class Worker {
private:
   char *workerName;
   char *SSN;
   Worker(const Worker&);
   Worker& operator=(const Worker&);
public:
   Worker();
   Worker(const char *workerName_, const char *SSN_);
   ~Worker();
   void setWorkerName(const char *newName);
   void setSSN(const char *newSSN);
   const char *getWorkerName() const { return workerName; }
   const char *getSSN() const { return SSN; }
   virtual void printValues() const;
};

請注意,除了公共析構函數之外,還添加了標記為private的副本構造函數和equals運算符。 這樣可以防止復制Worker ,因為沒有人可以調用它們。 (這是干杯們所說的“三個規則”。)

現在是您的默認構造函數。 你有:

Worker::Worker(){
   workerName = new char [40];
   SSN = " ";
}

這里的問題是您正在將workerName設置為指向新分配的(IIRC)歸零內存,並且您將SSN指向字符串常量。 您應該為兩者分配內存,並且該內存應該足夠大以容納該值。 這意味着分配一個char的兩個數組,其值為'\\0' (即,一個字符串,只是一個空終止符):

Worker::Worker(){
   workerName = new char [1];
   workerName[0] = '\0';
   SSN = new char [1];
   SSN[0] = '\0';
}

您也可以使用C ++初始化器語法來實現:

Worker::Worker(): workerName(new char [1]), SSN(new char [1]) {
   workerName[0] = '\0';
   SSN[0] = '\0';
}

接下來,您的兩個參數的構造函數。 除了使用strlen查找字符串的長度並使用strcpy復制它們之外,這里也一樣:

Worker::Worker(const char *workerName_, const char *SSN_) {
   workerName = new char [strlen(workerName_) + 1];
   strcpy(workerName, workerName_);
   SSN = new char [strlen(SSN_) + 1];
   strcpy(SSN, SSN_));
}

請注意,數組大小為“長度加一”,因為strlen末尾不計算'\\0'

還要注意,如果您像使用C字符串那樣使用mallocfree ,則可以只使用C的strdup函數:

Worker::Worker(const char *workerName_, const char *SSN_)
: workerName(strdup(workerName_)), SSN(strdup(SSN_)) {
}

但你不應該免費malloc與“d記憶delete[]你應該不自由new[]內存free

析構函數實際上很好,只需為SSN添加類似內容即可:

Worker::~Worker(){
   delete[] workerName;
   delete[] SSN;
}

我將保留“設置”功能,以供讀者練習。 請記住,他們需要1)刪除現有值,以及2)分配新值。 與上述步驟相同。

現在,如果您的專家要求將workerNamechar*並將SSNstring ...那么,混合上面的代碼來執行此操作對讀者來說是另一種練習。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM