簡體   English   中英

如何在C ++異常類析構函數中釋放變量

[英]How to free variable in C++ Exception class destructor

我定義它的一個新的C ++類的what方法返回char*與作為構造傳遞一個整數的值類型。

最初,我使用string類並從what返回字符串數據來完成此操作。

然后,我嘗試在以下代碼中使用char*類型:

/* Define the exception here */
class BadLengthException: public exception
{
  public:
    BadLengthException(int strLength)
    {
        strLen = strLength;
        res = (char*)malloc(strLength+1);
        int resultSize = sprintf(res, "%d", strLen);
    }
    ~BadLengthException() throw()
    {
        free(res);
    }
    virtual const char* what() const throw()
    {
      return res;
    }
  private:
    int strLen;
    char* res;
};

但是在釋放malloc分配的變量時遇到問題:它給出了以下異常:

pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

那為什么呢? 在哪里以及如何在Exception類中釋放動態分配的變量?

編輯

這里是一個最小的工作完整示例。 該程序將要求用戶輸入。 第一個是一個數字,指定以下輸入的數量。 其他輸入將是字符串。 如果字符串短於5,將引發上述異常。

只需輸入: 1 ,然后輸入Me

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

/* Define the exception here */
class BadLengthException: public exception
{
  public:
    BadLengthException(int strLength)
    {
        strLen = strLength;
        res = (char*)malloc(strLength+1);
        int resultSize = sprintf(res, "%d", strLen);
    }
    ~BadLengthException() throw()
    {
        free(res);
    }
    virtual const char* what() const throw()
    {
      return res;
    }
  private:
    int strLen;
    char* res;
};



bool checkUsername(string username) {
    bool isValid = true;
    int n = username.length();
    if(n < 5) {
        throw BadLengthException(n);
    }
    for(int i = 0; i < n-1; i++) {
        if(username[i] == 'w' && username[i+1] == 'w') {
            isValid = false;
        }
    }
    return isValid;
}

int main() {
    int T; cin >> T;
    while(T--) {
        string username;
        cin >> username;
        try {
            bool isValid = checkUsername(username);
            if(isValid) {
                cout << "Valid" << '\n';
            } else {
                cout << "Invalid" << '\n';
            }
        } catch (BadLengthException e) {
            cout << "Too short: " << e.what() << '\n';
        }
    }
    return 0;
}

編輯2

使用字符串原始類是:這個人做的工作

class BadLengthException: public exception
{
  public:
    BadLengthException(int strLength)
    {
        res = to_string(strLength);
    }
    virtual const char* what() const throw()
    {
      return res.c_str();
    }
  private:
    string res;
};

這與異常無關。 您的課程無法安全復制。

如果要編寫這樣的類,則需要使其遵循三個規則

發生的事情是您的異常對象正在被復制,它復制了指針,因此您將同一指針釋放了兩次。

但是,執行此操作的簡單方法是使用std::string而不是分配自己的內存。

class BadLengthException: public exception
{
public:
    BadLengthException(int strLength) : strLen(strLength), res(std::to_string(strLength))
    {
    }
    virtual const char* what() const throw()
    {
      return res.c_str();
    }
  private:
    int strLen;
    std::string res;
};

異常應該不會導致異常主題本身。 他們應該都是noexcept 因此,通常不建議動態內存分配/釋放-隱式(例如,使用std :: string ...)或顯式(新/刪除,malloc / free ...)。 一種更好的方法是使用靜態char數組:

class BadLengthException:
    public std::exception
{
public:
    BadLengthException(size_t len){
        std::snprintf(res, bufsz, fmt(), len);
    };
    ~BadLengthException()=default;
    virtual const char* what() const {return res;};
private:
    static auto& fmt() {return "bad length [%dz]";};
    static constexpr size_t fmtsz=sizeof(fmt());
    static constexpr size_t intsz=std::numeric_limits<size_t>::digits10;
    static constexpr size_t bufsz=fmtsz+intsz;
    char res[bufsz];
};

我將添加另一個答案,嘗試將所有內容放在一起。

問題

問題如下。 考慮以下示例:

double division(int a, int b) {
   if( b == 0 ) {
      throw "Division by zero condition!";
   }
   return (a/b);
}

在另一個塊中,該函數將被調用:

double c;
try
{
    c =division(d,f)
}
catch( ExceptionName e ) {
    ...
}

通常,異常在被調用的函數(上例中的division )中拋出(即,生成了實例),但是它們被代碼的其他部分(直接調用函數或什至在更多外部函數中)捕獲。 為了使異常實例從生成的地方到捕獲到的地方都可用,將復制該實例。

顯然,異常實例的復制是使用復制分配構造函數執行的( =需要清除)。 由於我沒有聲明一個,所以使用默認的一個。 這個默認的構造函數具有以下指針行為:而不是復制指針值,而是復制指針值本身(即地址),因此現在我有了另一個指向相同地址的實例。

假設上面的division函數中發生異常。 假設執行了實例的副本。 從函數返回時,原始實例將被銷毀,因為它位於堆棧內存中,因此將調用析構函數,並將釋放指針。 但是,指針的內存也由新副本共享。

當副本嘗試使用其指針時,將出現錯誤,因為它不是有效的指針。 在我的情況下,它的析構函數正在使用它試圖free它,但是它已經被釋放:因此是錯誤。

5法則

出現問題是因為我違反了C ++ 11中的5規則(以前是3規則)。

該規則指出,當手動使用和管理資源時(在我的情況下,內存就是資源),並且如果一個類實現以下成員之一,則另一個成員也應被覆蓋:

  1. 析構函數
  2. 復制構造函數
  3. 復制分配運算符
  4. 移動副本構造函數
  5. 移動分配運算符

4和5是2和3的對應物,但帶有rvalues這里是有關lvalues rvalues的好文章)

可能的解決方案

最簡單的方法是使用問題,注釋和答案中已指定的string ,以便由字符串類自動管理內存。

另一種選擇(如另一個答案中所述)是使用共享指針。

盡管不方便,但為了與問題保持一致,此處使用純指針實現。 重新構造了構造函數和析構函數,以便在復制構造函數中為其分配新的內存,而不是使用另一個實例分配的內存。

/* Define the exception here */
class BadLengthException: public exception
{
  public:
    BadLengthException(int strLength)
    {
        cout<<"Constructor\n";
        strLen = strLength;
        res = (char*)malloc(strLen+1);
        int resultSize = sprintf(res, "%d", strLen);
    }


    /*assignment operator*/
    BadLengthException& operator= (const BadLengthException &other)
    {
        cout<<"copy assignment constructor"<<endl;
        if(&other == this)
        {
            return *this;
        }
        strLen = other.strLen;
        res = (char*)malloc(strLen+1);
        int resultSize = sprintf(res, "%d", strLen);
        return *this;
    }

    /*copy constructor*/
    BadLengthException(BadLengthException& other)
    {
        cout<<"copy constructor\n";
        *this = other;

    }

    BadLengthException(BadLengthException&& other)
    {
        cout<<"move constructor "<<endl;
        *this = other;
    }


    BadLengthException& operator=(BadLengthException&& other)
    {
      cout<<"move assignment operator"<<endl;
      if(&other == this)
      {
          return *this;
      }
      *this = other;
      return *this;

    }

    /*class destructor*/
    ~BadLengthException() throw()
    {
        cout<<"destructor"<<endl;
        free(res);
    }

    virtual const char* what() const throw()
    {
      return res;
    }
  private:
    int strLen;
    char* res;
};

測試類的示例:

int main(int argc, char *argv[])
{
  {
    cout<<"-----Expecting copy constructor------\n";
    BadLengthException e(10);
    BadLengthException e1(e);
    cout<<"10: "<<e1.what()<<endl;
  }
  cout<<endl<<endl;
  {
    cout<<"-----Expecting copy assignment operator------\n";
    BadLengthException e(10);
    BadLengthException e2 = e;
    cout<<"10: "<<e2.what()<<endl;
  }
  cout<<endl<<endl;
  {
    cout<<"-----move assignment operator------\n";
    BadLengthException e3(1);
    e3 = BadLengthException(33);
  }
  {
    cout<<"-----move constructor------\n";
    BadLengthException e4 = BadLengthException(33);
  }
  {
    cout<<"-----move constructor------\n";
    BadLengthException e5(BadLengthException(33));
  }
  cout<<"-----6------\n";
  BadLengthException e6(1), e6_1(2);
  e6 = std::move(e6_1);
  return 0;
}

暫無
暫無

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

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