阐明关系运算符重载的概念(需要调试帮助)



让我告诉你们,我是c++的初学者
出于教育和学习目的,我创建了自己的字符串类MyString。根据导师的指示,我不允许使用标准库函数来比较两个字符串。MyString类包含字符型指针和整数型变量,后者保存字符串的长度,即:

class MyString{
char *str; int len;
public:
MyString(){
len = 1;
str = new char[len];
str[len - 1] = '';
}
MyString(char *p){
int count = 0;
for (int i = 0; p[i] != ''; i++){
count++;
}
len = count;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = p[i];
}
}
int length(){
return len;
}
bool operator < (MyString obj){
char temp;
if (len < obj.len){ return true; }
if (len>obj.len){ return false; }
if (this->len == obj.len){
for (int i = 0; i < len; i++){
if (this->str[i] < obj.str[i])
{
return true;
}
}
}
}
bool operator > (MyString obj) {
if (len > obj.len) {
return true;
}
if (len<obj.len) {
return false;
}
if (this->len == obj.len)
{
for (int i = 0; i < this->len; i++) {
if (this->str[i] > obj.str[i]) {
return true;
}
}
} 
}
bool operator == (MyString obj) {
int count = 0;
if (this->len == obj.len){
for (int i = 0; i < this->len; i++) {
if (this->str[i] == obj.str[i]) {
count++;
}
}
if (count == len) {
return true;
}
}
}
char & operator[](int i) {
return str[i];
}
};

这是主要的

int main()
{
char arr1[30], arr2[30];
cout << "Enter first MyString: ";
cin.get(arr1, 30);
cin.ignore();
cout << "Enter second MyString: ";
cin.get(arr2, 30);
MyString s1(arr1);    //parametrized constructor
MyString s2(arr2);
cout << "Length of s1:" << s1.length() << endl;
cout << "Length of s2:" << s2.length() << endl;
if (s1<s2)           // < operator overloaded
cout << "s1 < s2" << endl;
else if (s1>s2)      // > operator overloaded
cout << "s1 > s2" << endl;
else if (s1 == s2)     // == operator overloaded
cout << "s1 == s2" << endl;
return 0;
}

我比较两个字符串的算法是:

i( .如果len(s1的长度(小于obj.len(s2的长度(,则首先检查两个字符串的长度,然后返回true。

ii(.如果长度相等,则将s1字符数组的每个元素与s2字符数组进行比较。即使s1字符数组的一个元素小于s2字符数组的元素(ASCII(,也要返回true,否则返回false。

问题是每当程序执行时,无论传递的两个字符串是否相等,它都会在控制台上显示"s1<s2">

您正在尝试编写分配资源的简单类。这是一项非常重要的技能。到目前为止,您编写的代码有些不错,但也有很多错误。主要错误是

  1. 运算符的算法错误<。在你的代码"你好"<"再见"是不正确的
  2. 缺少返回语句
  3. 缺少析构函数,所以您的类会泄漏内存
  4. 一旦添加了析构函数,您还需要一个复制构造函数和复制赋值运算符,否则您的代码将因两次释放同一内存而崩溃,这被称为三规则。用谷歌搜索它,因为它可能是你将要阅读的C++建议中最重要的一条
  5. 缺乏对常量正确性的认识
  6. 缺乏通过参考的意识
  7. 对于过载的运算符,签名不太理想
  8. 你的课上缺少一些重要的方法
  9. 缺少一些实现技巧

将所有这些放在一起,按照给定的规则实现您的类,并附上一些注释以及

class MyString {
char *str; int len;
public:
// default constructor should create a empty string, i.e. a zero length string
MyString() {
len = 0;
str = new char[len];
}
// contents of p are not changed so make it const
MyString(const char *p) {
int count = 0;
for (int i = 0; p[i] != ''; i++){
count++;
}
len = count;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = p[i];
}
}
// destructor, frees memory
~MyString() {
delete[] str;
}
// copy constructor, similar to the above except it starts from a MyString
MyString(const MyString& o) {
len = o.len;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = o.str[i];
}
}
// swap method, efficient exchange of two strings
void swap(MyString& o) 
{
int t1 = o.len;
o.len = len;
len = t1;
char* t2 = o.str;
o.str = str;
str = t2;
}
// assignment operator, uses copy and swap idiom
MyString& operator=(MyString o) {
swap(o);
return *this;
}
// length does not modify the string, so it should be decalred const
int length() const {
return len;
}
char& operator[](int i) {
return str[i];
}
// need a const version of operator[] as well, otherwise you won't be able to do [] on a const string
char operator[](int i) const {
return str[i];
}
};
// operator< should be a function not a class method. This is the only way to get
// C++ to treat the two arguments symmetrically. For instance with your version
// "abc" < str is not legal, but str < "abc" is. This oddity is because C++ will
// not implicitly create a MyString object to call a MyString method but it will implicitly
// create a MyString object to pass a parameter. So if operator< is a function you will
// get implicit creation of MyString objects on either side and both "abc" < str and 
// str < "abc" are legal.
// You also should pass to parameters by const reference to avoid unnecessary
// copying of MyString objects.
// Finally this uses the conventional algorithm for operator<
bool operator<(const MyString& lhs, const MyString& rhs) {
for (int i = 0; ; ++i)
{
if (i == rhs.length())
return false;
if (i == lhs.length())
return true;
if (lhs[i] > rhs[i])
return false;
if (lhs[i] < rhs[i])
return true;
}
}
// This is the easy way to write operator>
bool operator>(const MyString& lhs, const MyString& rhs) {
return rhs < lhs;
}
// This is the easy way to write operator<=
bool operator<=(const MyString& lhs, const MyString& rhs) {
return !(rhs < lhs);
}
// This is the easy way to write operator>=
bool operator>=(const MyString& lhs, const MyString& rhs) {
return !(lhs < rhs);
}
// operator== is a function not a method for exactly the same reasons as operator<
bool operator==(const MyString& lhs, const MyString& rhs) {
if (lhs.length() != rhs.length())
return false;
for (int i = 0; i < lhs.length(); ++i)
if (lhs[i] != rhs[i])
return false;
return true;
}
// this is the easy way to write operator!=
bool operator!=(const MyString& lhs, const MyString& rhs) {
return !(lhs == rhs);
}

您的代码有无数问题,所以我将为您提供一个改进的、有评论的实现:

class MyString
{
char* str;
unsigned int len; // strings can't have negative length; using unsigned reflects this better
// even better: use size_t; this is the type for the concrete system
// able to cover any allocatable memory size
public:
MyString()
: str(new char[1]), len(1) // prefer initializer list
{
str[0] = 0; // does not matter if you use 0 or '', just my personal preference...
}
MyString(char const* p)
// you make a copy of, so have a const pointer (you can pass both const and non-const to)
{
for(len = 1; p[len] != 0; ++len);
//        ^ make sure to copy the terminating null character as well!
str = new char[len];
for (unsigned int i = 0; i < len; i++)
{
str[i] = p[i];
}
// or use memcpy, if allowed
}
// OK, above, you allocated memory, so you need to free it again:
~MyString() // if you want to be able to inherit from, it should be virtual;
// strings, though, most likely should not be inherited from...
{
delete[] str;
}
// C++ creates a default copy constructor; this one, however, just copies all members by value
// i. e. copies the POINTER str, but not the memory pointed to, i. e. does not perform a deep copy
// which is what you need, however, to avoid double deletion:
MyString(MyString const& other)
: str(new char[other.len]), len(other.len)
{
for (unsigned int i = 0; i < len; i++)
{
str[i] = other.str[i];
}
}
// similar for assignment; I'm using copy and swap idiom to reduce code duplication here:
MyString& operator=(MyString other)
{
swap(other);
return *this;
}
void swap(MyString& other)
{
char* str = this->str;
unsigned int len = this->len;
this->str = other.str;
this->len = other.len;
other.str = str;
other.len = len;
}
unsigned int length() const
//                    ^^^^^  allows to retrieve length from a
//                           const MyString as well!
{
return len;
}
// fine, you can change the character within the string
char& operator[](unsigned int i)
{
return str[i];
}
// but what, if you have a const MyString???
// solution:
char operator[](unsigned int i) const
//                              ^^^^^
{
return str[i];
}
// you could alternatively return a const reference,
// but char is just too small that a reference would be worth the effort
// additionally: a reference could have the const casted away by user
// which is not possible by returning a copy, so we gain a little of safety as well...
bool operator<(MyString const& other) const
//                      ^^^^^^
// we don't need a copy and don't want a copy(it would just costs runtime and memory for nothing)!
// -> pass by const reference
// additionally, we want to be able to do comparison on const this as well (see length)
// 
{
// have you noticed that you have one and the same code in all of your comparison operators???
// only the comparison itself changes lets have it just a little bit cleverer:
return compare(other) < 0;
}
bool operator>(MyString const& other) const
{
return compare(other) > 0;
}
bool operator==(MyString const& other) const
{
return compare(other) == 0;
}
// and for completeness:
bool operator<=(MyString const& other) const
{
return compare(other) <= 0;
}
bool operator>=(MyString const& other) const
{
return compare(other) >= 0;
}
bool operator!=(MyString const& other) const
{
return compare(other) != 0;
}
// the upcoming space ship operator (<=>) will simplify this, well, OK, but for now, we don't have it yet...
int compare(MyString const& other) const
{
// I decided to compare "abcd" smaller than "xyz" intentionally
// for demonstration purposes; just place your length checks
//  back to get your original comparison again
unsigned int pos = 0;
// EDIT: "stealing" john's implementation, as superior to
// mine (with minor adaptions) ... 
for (unsigned int pos = 0; ; ++pos)
{
///////////////////////////////////////////////////
// if you have your original length checks placed back above,
// just have the following check instead of the active one:
// if(pos == len) return 0;
if (pos == len)
{
return pos == other.len ? 0 : -pos - 1;
}
if (pos == other.len)
{
return pos + 1;
} 
///////////////////////////////////////////////////
if(str[pos] < other.str[pos])
{
return -pos - 1;
}
if(str[pos] > other.str[pos])
{
return pos + 1;
}
}
return 0;
}
// WARNING: above code has yet an issue! I wanted to allow (for demonstration)
// to return positional information so that we not only see the result of comparison
// but can conclude to at WHERE the two strings differ (but need 1-based offset for to
// distinguish from equality, thus addition/subtraction of 1);
// however, on VERY large strings (longer than std::numeric_limits<int>::max()/-[...]::min()), we get
// signed integer overflow on the implicit cast, which is undefined behaviour
// you might want to check against the limits and in case of overflow, just return the limits
// (leaving this to you...)
// alternative: just return -1, 0, +1, the issue is gone as well...
};

好的,您现在可以复制此代码,去掉注释并将其作为"您的"解决方案呈现。这不是我想要的答案!慢慢来,仔细阅读我的评论–你可以从…中学到很多东西。。。

最后:还有另一个可能的改进:在C++11之前,如果按值传递对象,则只能复制数据。由于C++,我们还可以将数据从一个对象移动到另一个对象–但是,该类型需要支持move语义。您可以通过额外提供移动构造函数和复制赋值来做到这一点:

MyString(MyString&& other)
: str(nullptr), len(0)
{
// delete[]'ing nullptr (in other!) is OK, so we don't need
// to add a check to destructor and just can swap again...
swap(other);
}
MyString& operator=(MyString&& other)
{
// and AGAIN, we just can swap;
// whatever this contained, other will clean it up...
swap(other);
return *this;
}

你可能有兴趣进一步阅读:

  • 三规则
  • 五条规则
  • 复制和交换习惯用法

在您的函数中,

bool operator < (MyString obj){

我看不出最后会返回false

它只有在到达第三个if时才返回true。

此外,正如其他人所提到的,长度并不意味着以您实现的方式进行比较。


只是一个注释:您的代码很容易发生内存泄漏。它分配但不释放内存。

此代码有很多错误:

  1. 没有创建复制构造函数和副本
  2. 缺少析构函数可以节省时间(内存泄漏,但正因为如此,点1没有崩溃(
  3. 空字符串的len = 1(默认构造函数(
  4. MyString(char *p)不添加终止字符
  5. 不使用const MyString &obj(不需要的副本(
  6. 方法的各个分支末尾缺少返回值
bool operator < (const MyString &obj) {
if (len < obj.len) {
return true;
}
if (len>obj.len) {
return false;
}
for (int i = 0; i < len; i++) {
if (this->str[i] != obj.str[i]) {
return this->str[i] < obj.str[i];
}
}
return false;
}

最新更新