【问题标题】:No output from using array in class在类中使用数组没有输出
【发布时间】:2019-09-29 07:19:54
【问题描述】:

我得到一个空白输出。我是新手,为此苦苦挣扎了一段时间。
我得到了编译器的 0 个错误。

还有什么可以改进的?

如何在不必使用static_cast 的情况下将const char* 的长度作为int 而不是size_t

#include <iostream>
#include <cassert>

class String
{
private:
    char* Str_Buffer{};
    int Str_Size{};
public:
    String(const char* string = " ")
        : Str_Size{ static_cast<int>(strlen(string)) }
    {
        Str_Buffer = new char[Str_Size];
    }

    String& operator=(const String& string)
    {
        if (this == &string)
            return *this;

        delete[] Str_Buffer;

        Str_Size = string.Str_Size;
        if (string.Str_Buffer)
        {
            Str_Buffer = new char[Str_Size];

            for (int index{ 0 }; index < Str_Size; ++index)
                Str_Buffer[index] = string.Str_Buffer[index];
        }
        return *this;
    }

    char& operator[](const int index)
    {
        assert(index >= 0);
        assert(index < Str_Size);
        return Str_Buffer[index];
    }

    friend std::ostream& operator<<(std::ostream& out, const String& string)
    {
        out << string.Str_Buffer;
        return out;
    }

    ~String()
    {
        delete[] Str_Buffer;
    }
};

int main()
{
    String word("Hello world!");
    std::cout << word;

    return 0;
}

【问题讨论】:

  • 你的代码doesn't compile
  • 对于strlen(),您必须包含&lt;cstring&gt;,它位于命名空间std中。

标签: c++ string class raii resource-management


【解决方案1】:

我得到一个空白输出。

您不会在构造函数中用有意义的数据填充String::Str_Buffer。您可以使用&lt;cstring&gt; 中的std::strcpy() 来执行此操作。 std::strlen() 也在该头文件中声明。要使用std::strcpy()String::Str_Buffer 指向的内存需要比您要复制到那里的字符串大一char,因为 C 和 C++ 中的字符串以零结尾 ('\0')。

如何在不必使用static_cast 的情况下将const char* 的长度作为int 而不是size_t

您为什么想要int? C++ 中对象的大小是用std::size_t 类型的值来衡量的(在几个头文件中定义,但有疑问时包括&lt;cstddef&gt;)。 std::size_t 保证足够大以处理所有对象大小。例如,std::strlen()sizeof 运算符的返回类型。

你的赋值运算符不是exception-safe

String& operator=(const String& string)
{
    // ...

    delete[] Str_Buffer;  // the old state is now gone

    Str_Size = string.Str_Size;
    if (string.Str_Buffer)
    {
        Str_Buffer = new char[Str_Size];  // when new[] throws, the object
                                          // will be in an undefined state
    // ...

可能但不优雅的解决方案:

String& operator=(const String& string)
{
    char *temp = new[string.Str_Size];

    // copy string.Str_Buffer to temp

    delete[] Str_Buffer;
    Str_Buffer = temp;
    Str_Size string.Str_Size

    return *this;
}

请参阅复制和交换以获得更好的解决方案。


资源管理

请熟悉The Rule of FiveCopy-and-Swap Idiom

管理字符串的类的起点可能如下所示:

#include <cassert>   // assert()
#include <cstddef>   // std::size_t
#include <cstring>   // std::strlen(), std::strcpy()
#include <utility>   // std::swap(), std::exchange()
#include <iostream>

class string_t
{
    size_t  length  = 0;
    char   *data    = nullptr;

public:
    string_t() = default;

    string_t(char const *str)
    : length { str ? std::strlen(str) : 0  },
      data   { new char[length + 1]{}      }
    {
        str && std::strcpy(data, str);
    }

    string_t(string_t const &other)  // copy constructor
    : length { other.length            },
      data   { new char[length + 1]{}  }
    {
        other.data && std::strcpy(data, other.data);
    }

    string_t(string_t &&other)  // move constructor
    : length { std::exchange(other.length, 0)      },  // steal others resources and
      data   { std::exchange(other.data, nullptr)  }   // give other a state it's
    {}                                                 // destructor can work with

    string_t& operator=(string_t other)   // assignment operator
    {                                     // mind: other gets copied
        std::swap(length, other.length);  // steal other's resources
        std::swap(data, other.data);      // other's destructor will
    }                                     // take care of ours.

    ~string_t() { delete[] data; }

    std::size_t get_length() const { return length; }

    char& operator[](std::size_t index)
    {
        assert(index < length);
        return data[index];
    }

    // stream-insertion operator:
    friend std::ostream& operator<<(std::ostream &os, string_t const &str)
    {
        return os << (str.data ? str.data : "");
    }
};

int main()
{
    string_t foo{ "Hello!" };  // char const* constructor
    std::cout << foo << '\n';

    string_t bar{ foo };  // copy constructor
    std::cout << bar << '\n';

    string_t qux{ string_t{ "World!" } };  // move constructor (from a temporary)
    std::cout << qux << '\n';

    bar = qux;  // assignment operator
    std::cout << bar << '\n';
}

【讨论】:

    【解决方案2】:

    首先,你需要包含用于 strlen。您得到一个空白输出,因为构造函数没有将输入字符串写入 Str_Buffer。您可以使用 std::copy 将内存复制到分配的缓冲区。

    你必须使用静态转换,因为 strlen 返回 std::size_t。只需将 Str_Size 的类型更改为 std::size_t 即可摆脱静态强制转换。

    还可以查看rule of five。定义移动和复制构造函数将提高代码的性能。

    在下面查看您的代码的工作版本:

    #include <iostream>
    #include <cassert>
    #include <cstring>
    #include <algorithm>
    
    
    class String
    {
    private:
        char* Str_Buffer;
        std::size_t Str_Size;
    public:
        String(const char* string = " ")
            : Str_Size{ strlen(string) }
        {
            Str_Buffer = new char[Str_Size];
            std::copy(string, string + Str_Size, Str_Buffer);
        }
    
        String(const String& other)
           : Str_Size(other.Str_Size)
        {
            Str_Buffer = new char[Str_Size];
            std::copy(other.Str_Buffer, other.Str_Buffer + Str_Size, Str_Buffer);
        }
    
        String(String && other)
        {
            *this = std::move(other);
        }
    
        String& operator=(const String& string)
        {
            if (this == &string)
                return *this;
    
            delete[] Str_Buffer;
    
            Str_Size = string.Str_Size;
            if (string.Str_Buffer)
            {
                Str_Buffer = new char[Str_Size];
    
                for (std::size_t index = 0; index < Str_Size; ++index)
                    Str_Buffer[index] = string.Str_Buffer[index];
            }
            return *this;
        }
    
        char& operator[](const int index)
        {
            assert(index >= 0);
            assert(index < Str_Size);
            return Str_Buffer[index];
        }
    
        friend std::ostream& operator<<(std::ostream& out, const String& string)
        {
            out << string.Str_Buffer;
            return out;
        }
    
        ~String()
        {
            delete[] Str_Buffer;
        }
    };
    
    int main()
    {
        String word("Hello world!");
        std::cout << word;
    
        return 0;
    }
    

    【讨论】:

    • std::copy 好多了,好多了。它将和memcpy 一样快(事实上,对于这样的类型,它等同于memmove),除了它是类型安全的,所以不会发生可怕和灾难性的破坏,并且可能默默地 当你改变你的类型时。
    • 定义“工作”。 Rule of Five
    • 嗯,工作时我的意思是“它会产生所需的输出”。当然,你们是完全正确的。我刚刚调整了答案。
    • 定义移动和复制构造函数将提高代码的性能。 - 没有。 需要自定义复制构造函数,因此可以安全地复制该类型的对象(深度复制)。默认的 copy-ctor 只复制指针值,当不止一个析构函数运行时,这将导致(除其他外)delete[] 两次使用相同的指针值。你的 move-ctor 应该将*this 的成员与other 的成员交换。为什么赋值运算符中的 for 循环?可以通过复制和交换进行异常保存。它是std::strlen()
    • out &lt;&lt; string.Str_Buffer; 无法正常工作,因为您的 Str_Buffer 不是以零结尾的。
    猜你喜欢
    • 1970-01-01
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多