我正在尝试自己练习C ++。
目前,我正在尝试实现运算符重载的概念,使其成为我自己的字符串类/函数。

我认为我正确地处理了这个问题;该代码可以成功运行。
但是我不确定我的代码是否以最佳方式编写(我是否需要将更多的方法/参数设为const?我是否会使用过多的代码?)并且我是否进行了良好的内存管理?

感谢所有反馈:)

#include <iostream>
#include <vector>
#include <string.h>
#include <stdio.h>
using namespace std;
class DynString {
private:
    char *c;

public:
    ~DynString() { delete[]c; }
    DynString() : c(NULL) {}
    //second constructor
    DynString(const char* input) {
        int x = strlen(input) + 1;
        c = new char[x];
        strcpy(c, input);
    }

    //(copyconstructor) TRIO //QUATRO
    DynString(const DynString& dynstring) {
        c = new char[strlen(dynstring.c) + 1];
        strcpy(this->c, dynstring.c);
    }
    //UNO
    friend ostream& operator<< (ostream& lhs, DynString& a) {
        lhs << a.c;
        return lhs;
    }
    //DUO
    char& operator[] (int i) {
        return c[i];
    }
    //TRIO
    DynString& operator+ (DynString& y) {
        char *newArray = new char[strlen(y.c) + strlen(this->c) + 1];
        strcpy(newArray, this->c);
        strcat(newArray, y.c);
        delete[] c;
        c = new char[strlen(newArray)];
        this->c = newArray; 
        return *this;
    }


    //QUATRO
    friend DynString operator+ (char* x, DynString& y) {
        DynString newString;
        newString.c = new char[strlen(x) + strlen(y.c) + 1];
        strcpy(newString.c, x);
        strcat(newString.c, y.c);

        return newString;
    }
    //QUATRO
    DynString& operator+ (char* x) {
        char *newArray = new char[strlen(x) + strlen(this->c) + 1];
        strcpy(newArray, this->c);
        strcat(newArray, x);
        delete[] c; 
        c = new char[strlen(newArray)];
        this->c = newArray;

        return *this;
    }
    //TRIO // QUATRO
    DynString& operator= (DynString& x) {
        char *newArray = new char[strlen(x.c) + 1];
        strcpy(newArray, x.c);
        delete[] c;
        c = new char[strlen(newArray)];
        this->c = newArray;
        return *this;
    }

};



int main()
{
    DynString stringA("Hello");
    DynString stringB(" Worlt");
    //UNO
    cout << stringA << stringB << std::endl;

    //DUO
    stringB[5] = 'd';

    //TRIO
    DynString stringC = stringA + stringB;
    cout << stringC << std::endl;

    DynString stringD;
    //QUATRO
    stringD = "The" + stringB + " Wide Web";
    cout << stringD << std::endl;

    system("pause");

    return 0;
}


评论

关于const-ness,需要更多的方法:)从const DynString开始(我希望从const开始),您会发现operator []不起作用。您需要两个运算符,一个用于只读访问,一个用于修改。与operator =相同-目前,您不能在右侧使用const对象!

只是为了避免混淆,您不需要两个版本的operator =-但需要将其参数设为const。而operator []确实需要两个版本:一个返回const char&,另一个返回char&。

@jvb为什么您希望他将从const开始? const适用于const之前的事物。 char * const x从右到左读取“ x是指向char的常量指针”。另一个示例:char const * x读取“ x是指向常量char的指针”。您建议以相反的顺序编写它只会阻止理解更复杂的情况。

@ doug65536我表示希望该源的作者可以开始使用const,因为找不到此关键字的单个实例。 -现在,请不要将此解释为使用建议。

@jvb啊,对不起,我误会了。

#1 楼

不要包含不必要的标头

我们没有使用<vector><stdio.h>,因此请将其删除。该类只需要<ostream>而不是<iostream>,并且我们更喜欢<cstring>而不是<string.h>来将标准函数放在其适当的命名空间中。 />这是一个坏习惯,会在较大的程序中引起问题。只是不要。

添加一些编译器警告

通过namespace std,我得到了一些有用的诊断,值得关注:

 g++ -Weffc++ 


通过将165851.cpp:33:39: warning: ‘DynString& DynString::operator+(DynString&)’ should return by value [-Weffc++] DynString& operator+ (DynString& y) { ^ 165851.cpp:54:34: warning: ‘DynString& DynString::operator+(char*)’ should return by value [-Weffc++] DynString& operator+ (char* x) { ^ 165851.cpp: In constructor ‘DynString::DynString(const char*)’: 165851.cpp:12:5: warning: ‘DynString::c’ should be initialized in the member initialization list [-Weffc++] DynString(const char* input) { ^~~~~~~~~ 165851.cpp: In copy constructor ‘DynString::DynString(const DynString&)’: 165851.cpp:19:5: warning: ‘DynString::c’ should be initialized in the member initialization list [-Weffc++] DynString(const DynString& dynstring) { ^~~~~~~~~ 165851.cpp: In function ‘int main()’: 165851.cpp:95:23: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wpedantic] stringD = "The" + stringB + " Wide Web"; ^~~~~~~ 165851.cpp:95:33: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wpedantic] stringD = "The" + stringB + " Wide Web"; ^~~~~~~~~~~ 更改为接受operator+而不是const char*,可以很容易地解决最后两个问题。 char*上的警告是要根据operator+来实现operator+(后者返回引用,但前者是对象)。 />如果评估operator+=会发生什么?在这两种情况下,内部指针都指向DynString() + DynString(),但是您很高兴在两者上都调用NULL-这是未定义的行为。以下是一些实际检查的代码:

DynString(const DynString& other)
    : c(nullptr)
{
    if (other.c) {
        c = new char[std::strlen(other.c) + 1];
        std::strcpy(c, other.c);
    }
}


但是总是分配内存可能更容易(更安全):

DynString(const char* input)
    : c(new char[(input ? std::strlen(input) : 0) + 1];)
{
    if (input) {
        std::strcpy(c, input);
    } else {
        *c = '
DynString(DynString&& other)
    : DynString()
{
    std::swap(c, other.c);
}
'; } }


然后所有方法都可以安全地假设strlen不为空。

添加move构造函数

这很容易:

char& operator[](size_t i) {
    return c[i];
}

const char& operator[](size_t i) const {
    return c[i];
}


重载c


允许常量和可变字符串对象:

    char *newArray = new char[strlen(y.c) + strlen(this->c) + 1];
    strcpy(newArray, this->c);
    strcat(newArray, y.c);
    delete[] c;
    c = new char[strlen(newArray)];
    this->c = newArray;


我相信operator[]size_t更适合作为索引类型。

消除内存泄漏

示例程序泄漏:

 int 


主要原因是:

    delete[] c;
    c = new char[strlen(newArray)];
    c = newArray;


如果您看到错误,则更明显从最后一行删除冗余==13517== Memcheck, a memory error detector ==13517== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==13517== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info ==13517== Command: ./165851 ==13517== Hello Worlt Hello World The World Wide Web sh: 1: pause: not found ==13517== ==13517== HEAP SUMMARY: ==13517== in use at exit: 47 bytes in 3 blocks ==13517== total heap usage: 12 allocs, 9 frees, 73,860 bytes allocated ==13517== ==13517== 11 bytes in 1 blocks are definitely lost in loss record 1 of 3 ==13517== at 0x4C2C93F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==13517== by 0x108FF3: DynString::operator+(DynString&) (165851.cpp:38) ==13517== by 0x108C62: main (165851.cpp:90) ==13517== ==13517== 18 bytes in 1 blocks are definitely lost in loss record 2 of 3 ==13517== at 0x4C2C93F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==13517== by 0x10915B: DynString::operator+(char*) (165851.cpp:59) ==13517== by 0x108CD5: main (165851.cpp:95) ==13517== ==13517== 18 bytes in 1 blocks are definitely lost in loss record 3 of 3 ==13517== at 0x4C2C93F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==13517== by 0x1091EF: DynString::operator=(DynString&) (165851.cpp:69) ==13517== by 0x108CE7: main (165851.cpp:95) ==13517== ==13517== LEAK SUMMARY: ==13517== definitely lost: 47 bytes in 3 blocks ==13517== indirectly lost: 0 bytes in 0 blocks ==13517== possibly lost: 0 bytes in 0 blocks ==13517== still reachable: 0 bytes in 0 blocks ==13517== suppressed: 0 bytes in 0 blocks ==13517== ==13517== For counts of detected and suppressed errors, rerun with: -v ==13517== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

public:
    DynString& operator+=(const DynString& other)
    {
        char *newArray = new char[strlen(c) + strlen(other.c) + 1];
        std::strcpy(newArray, c);
        std::strcat(newArray, other.c);
        delete[] c;
        c =  newArray;
        return *this;
    }

    DynString operator+(const DynString& other) const
    {
        DynString newString(*this);
        return newString += other;
    }


第一次分配显然已泄漏,应删除该行代码。在其他几种方法中也存在相同的错误。

this->的实现:

我会这样写,从其他版本需要的operator+开始:

DynString operator+(const char* x, const DynString& other) {
    DynString newString(x);
    return newString += other;
}


我们不需要+=,因为C风格的字符串将使用(非显式)构造函数提升为operator+(const char*)。我们确实需要重载以将C样式的字符串作为第一个参数-但它不必是朋友,因为它可以是普通的非成员函数:

#include <ostream>
#include <cstring>

class DynString {
private:
    char *c;

public:
    ~DynString()
    {
        delete[] c;
    }

    DynString(const char* input = nullptr)
        : c(new char[(input ? std::strlen(input) : 0) + 1])
    {
        if (input) {
            std::strcpy(c, input);
        } else {
            *c = '
private:
    DynString(const DynString& a, const DynString& b)
        : c(new char[std::strlen(a.c) + std::strlen(b.c) + 1])
    {
        std::strcpy(c, a.c);
        std::strcat(c, b.c);
    }

public:
    DynString operator+(const DynString& other) const
    {
        return DynString(*this, other);
    }
'; } } DynString(const DynString& other) : c(new char[std::strlen(other.c) + 1]) { std::strcpy(c, other.c); } DynString(DynString&& other) : DynString() { std::swap(c, other.c); } friend std::ostream& operator<<(std::ostream& lhs, const DynString& a) { return lhs << a.c; } char& operator[](size_t i) { return c[i]; } const char& operator[](size_t i) const { return c[i]; } DynString& operator+=(const DynString& other) { char *newArray = new char[strlen(c) + strlen(other.c) + 1]; std::strcpy(newArray, c); std::strcat(newArray, other.c); delete[] c; c = newArray; return *this; } DynString operator+(const DynString& other) const { DynString newString(*this); return newString += other; } DynString& operator=(DynString other) { std::swap(c, other.c); return *this; } }; DynString operator+(const char* x, DynString& other) { DynString newString(x); return newString += other; } #include <iostream> int main() { DynString stringA("Hello"); DynString stringB(" Worlt"); //UNO std::cout << stringA << stringB << std::endl; //DUO stringB[5] = 'd'; //TRIO DynString stringC = stringA + stringB; std::cout << stringC << std::endl; DynString stringD; //QUATRO stringD = "The" + stringB + " Wide Web"; std::cout << stringD << std::endl; }



改进版本

此代码已经过测试,不会泄漏:

void swap(DynString& other)
{
    std::swap(length, other.length);
    std::swap(c, other.c);
}

DynString& operator+=(const DynString& other)
{
    Dynstring newString(*this, other);
    swap(newString);
    return *this;
}



>使串联更有效

通过复制和附加来实现DynString效率低下,因为它为在附加期间立即释放的副本分配了内存。因此,我们可以重新考虑该方法并消除复制步骤。我会使用额外的构造函数来做到这一点:

private:
    std::size_t length;         // not including terminator
    char *c;

    DynString(const DynString& a, const DynString& b)
        : length(a.length + b.length),
          c(new char[length + 1])
    {
        std::memcpy(c, a.c, a.length);
        std::memcpy(c+a.length, b.c, b.length+1); // copies terminator
    }


我们现在可以简化operator+,并根据新的operator+=实现:

private:
    std::size_t length;         // not including terminator
    std::unique_ptr<char[]> c;

    DynString(const DynString& a, const DynString& b)
        : length(a.length + b.length),
          c(std::make_unique<char[]>(length + 1))
    {
        std::memcpy(c.get(), a.c.get(), a.length);
        std::memcpy(c.get()+a.length, b.c.get(), b.length+1); // copies terminator
    }


记住字符串的长度

如果我们将长度存储为成员,我们可以使每种方法更有效,并且允许在字符串中嵌入NUL字符:

#include <memory>

class DynString {
private:
    std::size_t length;         // not including terminator
    std::unique_ptr<char[]> c;

    // Helper constructor - allocates but does not initialize
    DynString(size_t length)
        : length(length),
          c(std::make_unique<char[]>(length + 1))
    {}

    DynString(const DynString& a, const DynString& b)
        : DynString(a.length + b.length)
    {
        std::memcpy(c.get(), a.c.get(), a.length);
        std::memcpy(c.get()+a.length, b.c.get(), b.length+1); // copies terminator
    }

public:
    DynString(const char* input = nullptr)
        : DynString(input ? std::strlen(input) : 0)
    {
        if (input) {
            std::memcpy(c.get(), input, length + 1);
        } else {
            c.get()[0] = 'q4312078q';
        }
    }

    DynString(const DynString& other)
        : DynString(other.length)
    {
        if (length != other.length)
            c = std::make_unique<char[]>(other.length+1);
        length = other.length;
        std::memcpy(c.get(), other.c.get(), length + 1);
    }

    DynString(DynString&& other) noexcept
        : DynString()
    {
        swap(other);
    }

    void swap(DynString& other) noexcept
    {
        std::swap(length, other.length);
        std::swap(c, other.c);
    }

    friend std::ostream& operator<<(std::ostream& lhs, const DynString& a)
    {
        return lhs << a.c.get();
    }


    char& operator[](size_t i) noexcept
    {
        return c.get()[i];
    }

    const char& operator[](size_t i) const noexcept
    {
        return c.get()[i];
    }


    DynString& operator+=(const DynString& other)
    {
        DynString newString(*this, other);
        swap(newString);
        return *this;
    }

    DynString operator+(const DynString& other) const
    {
        return DynString(*this, other);
    }


    DynString& operator=(DynString other) noexcept
    {
        swap(other);
        return *this;
    }
};


依靠内存管理的标准库

代替管理自己的内存,我们可以使operator+成为唯一的指针:

q4312078q

您会发现我们确实需要将对c的调用分散到需要指向内容的指针


智能指针版本:

(仅是类;除上面的第一个“改进版本”外,其他所有内容均保持不变,除了其他包含项)

q4312078q

评论


\ $ \ begingroup \ $
让我批评您的评论:1. c * -header不保证将其符号限制在命名空间std中。他们只保证也把它们放在那里。 2.用operator + =来实现operator +对于此类来说效率极低。
\ $ \ endgroup \ $
–重复数据删除器
17年6月15日在13:50

\ $ \ begingroup \ $
@Deduplicator,我并不是要暗示标头不得在命名空间std之外声明任何内容。我确实发现,如果我可以明确说明函数的来源,这会使我的代码更清晰。我想我可能还认为这可以减少意外使用保留标识符的风险,但这可能是虚幻的!至于效率,您当然就在那里,但是我想在进一步优化之前专注于正确性(而现实世界中的实现则更进一步,例如SSO)。
\ $ \ endgroup \ $
– Toby Speight
17年6月15日14:00

\ $ \ begingroup \ $
这确实是足够的。但是这些进一步的优化多少都取决于要计算的字符串...
\ $ \ endgroup \ $
–重复数据删除器
17年6月15日在14:11

\ $ \ begingroup \ $
当使用命名空间std显然过于时尚时,我仍然觉得这一点令人信服。 stackoverflow.com/questions/1452721/…为什么要从第三方库重载标准库函数名?
\ $ \ endgroup \ $
–解毒
17年6月15日在19:16



\ $ \ begingroup \ $
@Pysis一个简单的变量数据或计数足以使编译器抱怨。
\ $ \ endgroup \ $
–Rakete1111
17年6月16日在11:28

#2 楼


不要包含您不使用的内容。您只能使用<string.h><stdio.h>
虽然包括<vector>仅花费编译时间,但是包括<iostream>可能会增加可执行文件的大小和启动/关闭时间。
您应该避免using namespace std;这是一个困扰,只是出价以后该咬你了。为什么要“使用命名空间标准”;认为是不好的做法吗?
很抱歉,您的评论似乎完全没有用。至少我无法从中提取任何有用的信息。因此,清除它们。
您的DynString似乎是对0终止的字符串的薄包装。您确定您将永远不需要嵌入式零,也不必充分利用可能值得缓存的长度吗? />原始拥有指针是个坏主意。它们本身不仅易于泄漏,而且异常情况甚至会变得更加艰辛。
请考虑迁移到std::unique_ptr的免费抽象。
您的nullptr处理是不一致的。 nullptr UB是空字符串还是保留为单独的状态?
除此之外,nullptr优于NULL0。它不仅具有独特的类型,而且也不能与整数混淆。
std::size_t用于索引和大小。就是这样。
您忘记在允许的位置标记函数noexcept
您的默认构造函数应标记为constexpr,或者分配一个长度为0的字符串。
DynString(const char*)中您计算长度分配适当数量的输入。为什么您不将其重用于高效的memcpy而不是性能较低的strcpy
至少允许将其用于隐式转换是有意义的。否则,您必须将其标记为explicit
DynString(const DynString&)应该委托给DynString(const char*),而不是将所有内容都写遍。
您缺少移动指针。
stream-output-operator不会修改第二个参数,因此应该通过常量引用来接受它。
您缺少operator []的常量重载。而且无论如何,索引应该是std::size_t而不是int

您的operator+(DynString&)(还有operator+(const char*))是一年级的混乱:


它应该是按值返回一个新的DynString,而不是按引用返回其中一个参数。
两个参数都应为常量。
您不应在其中分配第二次时间以泄漏它。此外,第二个分配也可能抛出...
它应该是一个朋友函数,因此可以在其第一个参数上进行转换,委托给连接两个const char*的私有方法。这样一来,您就可以实现两个变体,用const char*替换其中一个参数以提高效率,而无需重复。
为什么不重用您计算出的字符串长度? memcpystrcpy更有效。


至少operator+ (char* x, DynString& y)没错,只有效率低。
考虑添加operator+=(两种效率形式),分别根据operator+operator=实现。
您的operator=不应该接受左值引用。按值接受参数并使用复制和交换。这样一来,您还将丢失thememory-leak。
您真的应该添加更多观察者,例如:.data().size().begin().end()至少。

修改后的代码,确定nullptr是UB ,并添加一些附加功能:

#include <cstring>
#include <algorithm>
#include <utility>
#include <iterator>
#include <vector>

class DynString {
    char *c;
    DynString(std::picewise_construct_t, const char* a, const char* b) : c() {
        auto na = std::strlen(a), nb = std::strlen(b);
        c = new char[a + b + 1 > a ? a + b + 1 : SIZE_MAX];
        std::copy_n(a, na, c);
        std::copy_n(b, nb + 1, c + na);
    }
    template<class It, decltype((void)(*c = *std::declval<It>()))...>
    DynString(std::forward_iterator_tag, It first, It last) : c() {
        auto n = std::distance(first, last);
        c = new char[0 <= n && n < SIZE_MAX ? std::size_t(n) + 1 : SIZE_MAX];
        std::copy_n(first, std::size_t(n), c);
        c[n] = 0;
    }
    template<class It, decltype((void)(*c = *std::declval<It>()))...>
    DynString(std::input_iterator_tag, It first, It last) : c() {
        std::vector<char> v(first, last);
        swap({v.begin(), v.end()});
    }
public:
    DynString() : c(new char[1]{}) {}
    DynString(const char* s) : DynString(s, s + std::strlen(s)) {}
    DynString(const DynString& s) : DynString(s.c) {}
    DynString(DynString&& s) : DynString() { swap(s); }
    template<class It, decltype((void)DynString(std::iterator_traits<It>::iterator_category(), first, last))...>
    DynString(It first, It last) : DynString(std::iterator_traits<It>::iterator_category(), first, last) {}
    ~DynString() { delete[] c; }
    friend ostream& operator<<(ostream& lhs, const DynString& a) { return lhs << a.c; }
    char& operator[](std::size_t i) noexcept { return c[i]; }
    const char& operator[](std::size_t i) const noexcept { return c[i]; }
    auto data() noexcept { return c; }
    auto data() const noexcept { return c; }
    auto begin() noexcept { return c; }
    auto begin() const noexcept { return c; }
    auto end() noexcept { return c + size(); }
    auto end() const noexcept { return c + size(); }
    auto rbegin() noexcept { return std::make_reverse_iterator(end()); }
    auto rbegin() const noexcept { return std::make_reverse_iterator(end()); }
    auto rend() noexcept { return std::make_reverse_iterator(begin()); }
    auto rend() const noexcept { return std::make_reverse_iterator(begin()); }
    auto size() const noexcept { return std::strlen(c); }
    friend DynString operator+(const DynString& a, const DynString& b)
    { return {std::piecewise_construct, a.c, b.c}; }
    friend DynString operator+(const char* a, const DynString& b)
    { return {std::piecewise_construct, a, b.c}; }
    friend DynString operator+(const DynString& a, const char* b)
    { return {std::piecewise_construct, a.c, b}; }
    DynString& operator+=(const DynString& s) { return *this += s.c; }
    DynString& operator+=(const char* s) { return *this = *this + s; }
    DynString& operator=(DynString s) { swap(s); return *this; }
    void swap(DynString& o) noexcept { using std::swap; swap(c, o.c); }
};


评论


\ $ \ begingroup \ $
我想在回答中说一些关于嵌入式'\ 0'的内容,但我忘了。现在我不需要;-)
\ $ \ endgroup \ $
– Toby Speight
17年6月15日在13:51

#3 楼

这里的内存泄漏(在operator+中):

 c = new char[strlen(newArray)];
 this->c = newArray;


,因为cthis->c之间没有区别。该代码将覆盖指向new分配的内存的指针。

#4 楼

我认为错过了其他答案的一件事:您正在使用strcatstrcpystrlen。是2017年,您正在编写C ++-绝对不要这样做。



而不是像在1978年一样编写C ++一样,请使用strn*函数家族:strncatstrncpy和一个强大的实现:Microsoft的strnlen如果使用的是strnlen(Linux,某些BSD等),则为GNU glibc。此外,在C ++中,您还可以使用std::copystd::find之类的漂亮函数。

这样做使您的代码本来就更安全(更不容易发生缓冲区溢出),更可预测和更可靠,并且更现代。

评论


\ $ \ begingroup \ $
注意:您也不必在C中使用那些函数的旧版本。
\ $ \ endgroup \ $
–大卫·康拉德(David Conrad)
17年6月16日在7:02

\ $ \ begingroup \ $
为什么不加倍努力,完全清除str *函数,并使用适当的C ++算法?即std :: copy(用于复制和连接),std :: find(用于长度)。诚然,如果存在,我会使用std :: strnlen;但事实并非如此。我想如果您在未知大小的缓冲区上操作std :: strlen是可以的(但那不应该发生)。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
17年6月16日在9:17



\ $ \ begingroup \ $
@DavidConrad我首先是C程序员,一点都不是C ++程序员,所以这就是我对strn *情况的了解:)
\ $ \ endgroup \ $
–猫
17年6月16日在12:51

\ $ \ begingroup \ $
@Deduplicator对不起,什么?您是否只是说过有时可以使用这些功能的不安全且已弃用的版本?
\ $ \ endgroup \ $
–猫
17年6月16日在12:53

\ $ \ begingroup \ $
@cat:首先,只有MS弃用了它们,这很痛苦。其次,使用正确的工具完成工作,而实际上这绝不是这些功能。您是否曾经仔细研究过它们的API定义,尤其是strncpy和strncat?无论如何,str * cat遭受了画家Shlemiel的严重伤害! (使用strcpy仅表现出较弱的症状,如果有长度,则应使用std :: copy_n等。)
\ $ \ endgroup \ $
–重复数据删除器
17年6月16日在14:27

#5 楼

operator +实现有几个主要错误。请考虑以下情况,其中A,B和C在当前范围内是DynString类型:

DynString& operator+ (DynString& y) {
    char *newArray = new char[strlen(y.c) + strlen(this->c) + 1];
    strcpy(newArray, this->c);
    strcat(newArray, y.c);
    delete[] c;
    c = new char[strlen(newArray)];
    this->c = newArray; 
    return *this;
}


现在考虑: >



DynString A = "foo";
DynString B = "bar";
A = A + B;  //this example works and does what it looks like it should


这些示例具有意外的副作用。运行此块后,字符串存储以下值:

DynString A = "foo";
DynString B = "bar";
DynString C = A + B;  //this example not work like it should


最后,我们返回引用,因此请考虑这种情况:

运行上述情况后,我们期望B是“ foobardude”,而我们对A的期望取决于我们是否意识到上一示例中的错误。我们应该期望A是“ foo”或“ foobar”,因为应该返回一个临时值。两者都不是,A将是“ foobardude”。如所写,该实现不允许在不更改变量原始值的情况下进行串联。

#6 楼

即使没有分配内存,析构函数也会尝试释放内存。也许创建一个布尔条件进行检查。

评论


\ $ \ begingroup \ $
这不是缺陷。请记住,删除空指针是无操作的。
\ $ \ endgroup \ $
–重复数据删除器
17年6月15日在16:13