这是我自己练习写的一个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有任何影响。
所以,请给我指出来问题出在哪里了,我真的很困惑!谢谢!