簡體   English   中英

來自字符串的 C++ 子字符串

[英]C++ substring from string

我對 C++ 很陌生,我需要創建 MyString 類,以及從另一個子字符串創建新 MyString 對象的方法,但是在創建類時以及使用我的方法打印它時,選擇的子字符串會發生變化。

這是我的代碼:

#include <iostream>
#include <cstring>

using namespace std;

class MyString {
public:
    char* str;

    MyString(char* str2create){
        str = str2create;
    }

    MyString Substr(int index2start, int length) {
        char substr[length];
        int i = 0;
        while(i < length) {
            substr[i] = str[index2start + i];
            i++;
        }
        cout<<substr<<endl; // prints normal string
        return MyString(substr);
    }

    void Print() {
        cout<<str<<endl;
    }
};

int main() {
    char str[] = {"hi, I'm a string"};
    MyString myStr = MyString(str);
    myStr.Print();

    MyString myStr1 = myStr.Substr(10, 7);
    cout<<myStr1.str<<endl;
    cout<<"here is the substring I've done:"<<endl;
    myStr1.Print();

    return 0;
}

這是輸出:

嗨,我是一個字符串

細繩

斯特里

這是我完成的子字符串:

您的函數Substr通過在返回值MyString對象中存儲指向它的指針來間接返回局部變量substr的地址。 一旦超出范圍,取消引用指向局部變量的指針是無效的。

我建議您決定您的類是包裝外部字符串,還是擁有自己的字符串數據,在這種情況下,您需要將輸入字符串復制到成員緩沖區。

必須通過這個來解釋正確的地方出了什么問題,所以請耐心等待。

int main() {
    char str[] = {"hi, I'm a string"};

分配一個 17 個字符的臨時數組(16 個字母加上一個終止空值),在其中放置字符“嗨,我是一個字符串”,並以空值結束。 臨時意味着它聽起來像什么。 當函數結束時, str消失了。 任何指向str東西現在都指向垃圾。 在被重用和覆蓋之前,它可能會蹣跚而行,並提供一些表面上的生命,但實際上它是一個僵屍,只能信任殺死您的程序並吃掉它的大腦。

    MyString myStr = MyString(str);

創建另一個臨時變量 myStr。 使用字符數組調用構造函數。 那么讓我們來看看構造函數:

MyString(char* str2create){
    str = str2create;
}

取一個指向字符的指針,在這種情況下,它將有一個指向 main 的str的第一個元素的指針。 該指針將分配給 MyString 的str 沒有復制“嗨,我是一個字符串”。 mains 的str和 MyString 的str指向內存中的同一位置。 這是一種危險的情況,因為對一個的改變會影響另一個。 如果一個str消失,另一個str消失。 如果一個str被覆蓋,另一個str也會被覆蓋。

構造函數應該做的是:

MyString(char* str2create){
    size_t len = strlen(str2create); // 
    str = new char[len+1]; // create appropriately sized buffer to hold string
                           // +1 to hold the null
    strcpy(str, str2create); // copy source string to MyString
}

一些警告:這真的很容易破解。 例如,傳入一個永遠不會結束的 str2create,並且 strlen 將旋轉到未分配的內存中,結果將是不可預測的。

現在我們假設沒有人特別惡意並且只會輸入好的值,但這在現實世界中已被證明是非常糟糕的假設。

這也強制要求析構函數釋放str使用的內存

virtual ~MyString(){
    delete[] str;
}

它還增加了對復制和移動構造函數以及復制和移動賦值運算符的要求,以避免違反三/五規則

回到 OP 的代碼...

strmyStr指向內存中的同一個位置,但這還不錯。 因為這個程序是微不足道的,所以它永遠不會成為問題。 myStrstr都在同一點到期,並且都不會再次修改。

myStr.Print();

將正確打印,因為strmyStr沒有任何變化。

    MyString myStr1 = myStr.Substr(10, 7);

需要我們查看 MyString::Substr 來看看會發生什么。

MyString Substr(int index2start, int length) {
    char substr[length];

創建一個長度為長度的臨時字符數組。 首先,這是非標准的 C++。 它不會在很多編譯器下編譯,首先不要這樣做。 第二,是暫時的。 當函數結束時,這個值是垃圾。 不要使用任何指向substr指針,因為它的存在時間不足以使用它們。 第三,沒有為終止空值保留空間。 這個字符串將是一個等待發生的緩沖區溢出。

    int i = 0;
    while(i < length) {
        substr[i] = str[index2start + i];
        i++;
    }

好的,這很好。 從源復制到目標。 它缺少的是空終止,因此 char 數組的用戶知道它何時結束。

    cout<<substr<<endl; // prints normal string

那個緩沖區溢出等待發生? 剛發生。 哎呀。 你很不走運,因為它看起來像它工作而不是崩潰並讓你知道它沒有。 在內存中的正確位置必須是空值。

    return MyString(substr);

這創建了一個新的 MyString 指向substr 就在substr到達函數末尾並死亡之前。 這個新的 MyString 幾乎立即指向垃圾。

}

Substr 應該做什么:

MyString Substr(int index2start, int length)
{
    std::unique_ptr<char[]> substr(new char[length + 1]);
    // unique_ptr is probably paranoid overkill, but if something does go 
    // wrong, the array's destruction is virtually guaranteed
    int i = 0;
    while (i < length)
    {
        substr[i] = str[index2start + i];
        i++;
    }
    substr[length] = '\0';// null terminate
    cout<<substr.get()<<endl; // get() gets the array out of the unique_ptr
    return MyString(substr.get()); // google "copy elision" for more information 
                                   // on this line.
}

回到 OP 的代碼中,隨着返回到主函數, substr開始被重用和覆蓋。

cout<<myStr1.str<<endl;

打印myStr1.str並且我們已經可以看到其中一些已被重用和銷毀。

cout<<"here is the substring I've done:"<<endl;
myStr1.Print();

更多的死亡,更多的破壞,更少的繩索。

以后不要做的事情:

共享數據應該被復制的指針。

指向臨時數據的指針。

非空終止字符串。

暫無
暫無

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

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