婉瑄|Vivian_ 2022-12-14 17:51 采纳率: 100%
浏览 114
已结题

自己写的String类有segmentation fault,求解答!

这是我自己练习写的一个String类(有点长,问题在底下):

#include <iostream>

class String
{
public:
    /* Special functions */
    // default constructor (init to "")
    String() noexcept;
    // implicit conversion constructor (convert from const char* to String)
    String(const char* __str) noexcept;
    // user-defined destructor
    ~String() noexcept;
    // copy constructor
    String(const String& __other) noexcept;
    // copy assignment op
    String& operator=(const String& __other) noexcept;
    // move constructor
    String(String&& __other) noexcept;
    // move assignment op
    String& operator=(String&& __other) noexcept;
    // member indexing op (non-const version)
    char& operator[](const std::uint32_t __idx);
    // member indexing op (const version)
    const char& operator[](const std::uint32_t __idx) const;
    // implicit conversion function (natual conversion from String to const char*)
    operator char*() const noexcept;

    /* Class member functions */
    // is_empty method (find if the string is empty)
    bool is_empty() const noexcept;
    // size method (get the string size)
    std::uint32_t size() const noexcept;
    // clear method (clear the string to NULL)
    void clear() noexcept;

    // Index method (find the first index which the sequence appears)
    std::int32_t Index(const char* __substr) const noexcept;
    // toUpperCase (change every character to upper case char)
    const String toUpperCase() const noexcept;
    // replace method (replace Something with Other)
    const String replace(const char* __in, const char* __out) const noexcept;

    /* OTHER METHODS CAN BE EXTENDED... */

private:
    char* m_str;
    std::uint32_t m_size;
};


// default constructor (init to "")
String::String() noexcept : m_str(nullptr), m_size(0) {}

// implicit conversion constructor (convert from const char* to String)
String::String(const char* __str) noexcept
{
    // get size of input str
    for(
        char* temp = const_cast<char*>(__str);
        *temp != '\0';
        m_size++, temp++
    );
    // heap allocation
    m_str = new char[m_size];
    for(int i = 0; i < m_size; i++)
        m_str[i] = *(__str + i);
}

// user-defined destructor
String::~String() noexcept
{
    delete[] this->m_str;   // safe to delete nullptr
    this->m_str = nullptr;
    this->m_size = 0;
}

// copy constructor
String::String(const String& __other) noexcept
{
    m_size = __other.m_size;
    m_str = new char[m_size];
    for(int i = 0; i < m_size; i++)
        m_str[i] = *(__other.m_str + i);
}

// copy assignment op
String& String::operator=(const String& __other) noexcept
{
    // check if *this res can be reused
    if(m_size != __other.m_size)
    {
        this->~String();    // free *this res
        m_size = __other.m_size;
        m_str = new char[m_size];
    }
    for(int i = 0; i < m_size; i++)
        m_str[i] = *(__other.m_str + i);
    return *this;
}

// move constructor
String::String(String&& __other) noexcept
{
    m_str = std::__exchange<char*>(__other.m_str, nullptr);
    m_size = std::__exchange<std::uint32_t>(__other.m_size, 0);
}

// move assignment op
String& String::operator=(String&& __other) noexcept
{
    // guard self assignment
    if(this == &__other)
        return *this;
    delete[] m_str; // prevent from mem leak
    m_str = std::__exchange<char*>(__other.m_str, nullptr);
    m_size = std::__exchange<std::uint32_t>(__other.m_size, 0);
    return *this;
}

// member indexing op (non-const version)
inline char& String::operator[](const std::uint32_t __idx)
try
{
    if(m_size == 0)
        throw std::domain_error("Error when accessing empty string! \n");
    else if(__idx > m_size - 1)
        throw std::domain_error("Index out of range! \n");
    // safely return by ref
    return m_str[__idx];
}
catch(const std::domain_error& e)
{
    std::cout << "File: " << __FILE__ << '\n'
              << "Line: " << __LINE__ << '\n'
              << "What: " << e.what() << '\n';
    throw;
}

// member indexing op (const version)
const char& String::operator[](const std::uint32_t __idx) const
try
{
    if(m_size == 0)
        throw std::domain_error("Error when accessing empty string! \n");
    else if(__idx > m_size - 1)
        throw std::domain_error("Index out of range! \n");
    // safely return by const ref
    return m_str[__idx];
}
catch(const std::domain_error& e)
{
    std::cout << "File: " << __FILE__ << '\n'
              << "Line: " << __LINE__ << '\n'
              << "What: " << e.what() << '\n';
    throw;
}

// implicit conversion function (natual conversion from String to const char*)
String::operator char*() const noexcept
{
    return this->m_str;
}

// is_empty method (find if the string is empty)
inline bool String::is_empty() const noexcept
{
    return (m_size == 0 && m_str == nullptr);
}

// size method (get the string size)
inline std::uint32_t String::size() const noexcept
{
    return m_size;
}

// clear method (clear the string to NULL)
inline void String::clear() noexcept
{
    this->~String();
}

// Index method (find the first index which the sequence appears)
std::int32_t String::Index(const char* __substr) const noexcept
{
    // get the size of substr
    // get size of input str
    std::uint32_t subsize = 0;
    for(
        char* temp = const_cast<char*>(__substr);
        *temp != '\0'; 
        temp++, subsize++
    );
    // contain no substr if substr is longer than mainstr
    if(subsize > this->m_size)
        return -1;
    for(int i = 0; i <= m_size - subsize; i++)
    {
        int j = 0;
        for(; j < subsize; j++)
        {
            if(__substr[j] != m_str[i + j])
                break;
        }
        if(j == subsize)
            return i;
    }
    return -1;
}

// toUpperCase (change every character to upper case char)
const String String::toUpperCase() const noexcept
{
    String temp(*this);
    for(int i = 0; i < temp.m_size; i++)
    {
        if(temp[i] >= 'a' && temp[i] <= 'z')
            temp[i] -= 32;
    }
    return temp;
}

// replace method (replace Something with Other)
const String String::replace(const char* __in, const char* __out) const noexcept
{
    String temp(*this);
    for(int i = 0; i < temp.m_size; i++)
    {
        if(temp[i] == __in[0])
            temp[i] = __out[0];
    }
    return temp;
}

这是我用来测试的测试代码:

#define TEST
#ifdef TEST
int main()
{
    String str1 = "hello";        // implicit conversion ctor
    [[maybe_unused]] String str2; // default ctor
    String str3 = str1;           // copy ctor

    std::cout << str3 << '\n';              // implicit conversion op
    std::cout << str3.Index("llo") << '\n'; // output 2
    std::cout << str3.toUpperCase() << '\n';    // output HELLO
    std::cout << str3.replace("l", "O") << '\n';  // output heOOo
}
#endif

问题是,当我在运行这段代码的时候,总是会出现segmentation fault,也就是说我修改了不属于我的内存;
但是,当把

 [[maybe_unused]] String str2; // default ctor

这行代码注释掉之后,程序又能正常运行了(这说明拷贝构造函数没问题)。
但按道理说,加上那句代码也只是用默认构造函数创建了一个str2,应该不会对str1和str3有任何影响。

所以,请给我指出来问题出在哪里了,我真的很困惑!谢谢!

  • 写回答

2条回答 默认 最新

  • 真相重于对错 2022-12-14 20:25
    关注

    你的类是对char* 的封装,但你所有的操作都没有把结尾赋值'\0'.
    其次c++ operator[] 只有以下俩种

    R& T::operator[](S b);
    R& T::operator[](S1 s1, ...);
    
    (C++23 起)
    N/A
    
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 12月22日
  • 已采纳回答 12月14日
  • 创建了问题 12月14日

悬赏问题

  • ¥15 救!ENVI5.6深度学习初始化模型报错怎么办?
  • ¥30 eclipse开启服务后,网页无法打开
  • ¥30 雷达辐射源信号参考模型
  • ¥15 html+css+js如何实现这样子的效果?
  • ¥15 STM32单片机自主设计
  • ¥15 如何在node.js中或者java中给wav格式的音频编码成sil格式呢
  • ¥15 不小心不正规的开发公司导致不给我们y码,
  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000