固定大小字符串的最佳实践 Class

Best Practice for Fixed Size String Class

我想要一个固定大小的字符串 class。理想情况下,该接口将与 std::string 中的一个相匹配,唯一的区别是新的 class 从不分配新内存。对于应避免分配新内存的应用程序,它应该是一个方便的 class。大小可以是静态的(在编译时已知)。

我认为有两种方法。第一种是围绕 char 数组实现 class,然后或多或少地实现 std::string 具有的所有功能。我还必须实现一些运算符来创建具有给定固定大小字符串的 std::string,等等

第二种方法,我什至不确定是否可行,是从 std::string and 继承,覆盖所有可能改变字符串大小的函数。我查看了 Visual Studio 中的 basic_string header,它似乎不是虚拟的,所以我想这不是要走的路。

您认为实施此类 class 的最佳方法是什么?

The first would be to implement a class around a char array and then implement more or less all the functions that the std::string has.

这绝对是正确的方法。易于编写,易于使用,不易误用。

template <size_t N>
class fixed_string {
    char array[N+1];
    size_t size;

public:
    fixed_string() : size(0) { array[0] = '[=10=]'; }

    // all the special members can be defaulted
    fixed_string(fixed_string const&) = default;
    fixed_string(fixed_string&&) = default;
    fixed_string& operator=(fixed_string const&) = default;
    fixed_string& operator=(fixed_string&&) = default;
    ~fixed_string() = default;

    // ...
};

所有访问器(datac_strbeginendatoperator[])都是单行的。所有搜索算法都很简单。

唯一真正的设计问题是您希望突变在失败时做什么。即:

fixed_string<5> foo("abcde");
foo += 'f'; // assert? throw? range-check internally and ignore?
            // just not even add this and instead write a 
            // try_append() that returns optional<fixed_string&>?

设计选择有利也有弊,但无论选择哪一种,每个功能的实现也要非常简洁。


The second method, I'm not even sure is possible, would be to inherit from std::string and override all the functions that may change the size of the string. I looked into the basic_string header in Visual Studio and it doesn't seem to be virtual, so I guess this is not the way to go.

std::string 中的任何内容是否是 virtual 与这是否是一个好主意的问题无关。您肯定希望从以下位置开始:

template <size_t N>
class fixed_string : private std::string { ... }
//                  ^^^^^^^^^

因为你的类型肯定不符合 std::string 的 is-a 关系。它不是 std::string,它只是根据它来实现。私有继承会使这段代码格式错误:

std::string* p = new fixed_string<5>();

这样您就不用担心缺少virtual.

也就是说,从 string 继承将导致比直接走的路线更复杂、效率更低的实施,并且有更多潜在的陷阱。实现这样的事情可能可能,但我看不出这是个好主意。

我继续制作了一个简单的 class 可以用来构建。结构是这样的:base class 是一个仅声明接口,它只包含你想要拥有的构造函数类型的签名,以及继承 [=] 中必须实现的所有函数的列表18=] - classes 因为它们是纯虚拟的。派生的 class 是一个具有实际实现的模板 class。你不直接使用 class 因为有一个辅助函数模板,它采用你想要为你想要支持的每个构造函数类型传递的类型,并且它 returns 该类型。

#ifndef FIXED_STRING_H
#define FIXED_STRING_H

#include <string>

// This base class does not contain any member variables 
// and no implementations of any constructor or function
// it serves as a definition to your interface as well as
// defining what methods must be implemented.
class fixed_string_base {
protected:
    // The types of constructors you want to implement
    template<size_t fixed_size>
    explicit fixed_string_base( const char(&words)[fixed_size] ) {};

    // The types of things you want to leave to default
    fixed_string_base() = default;
    fixed_string_base( fixed_string_base const& ) = default;
    fixed_string_base( fixed_string_base&& ) = default;
    fixed_string_base& operator=( fixed_string_base const& ) = default; 
    fixed_string_base& operator=( fixed_string_base&& ) = default;
    virtual ~fixed_string_base() = default;
public:
    // Put all of your pure virtual methods here that fixed_string must implement;
    virtual char* c_str() = 0;
    virtual size_t size() const = 0;
    virtual size_t count() const = 0;
};

// This is the actual class that inherits from its non
// templated declaration interface that has the implementation of the needed constructor(s)
// and functions or methods that were declared purely virtual in the base class
template<size_t fixed_size>
class fixed_string_t  : public fixed_string_base {
private:
    size_t fixed_string_size_t = fixed_size;
    char fixed_string_[fixed_size];

public:
    //template<size_t fixed_size>
    explicit fixed_string_t( const char(&words)[fixed_size] ) {
        strncpy_s( fixed_string_, sizeof(char) * (fixed_size), &words[0], fixed_string_size_t );
        fixed_string_[fixed_size] = '[=10=]';
    }

    // c_str returns the character array.
    virtual char*  c_str() { return fixed_string_; }
    // size gives the total size including the null terminator
    virtual size_t size() const { return fixed_string_size_t; } 
    // count gives the size of the actual string without the null terminator
    virtual size_t count() const { return fixed_string_size_t - 1; }

    // Defaulted Constructors and Operators
    fixed_string_t( fixed_string_t const& ) = default;
    fixed_string_t( fixed_string_t&& ) = default;
    fixed_string_t& operator=( fixed_string_t const& ) = default;
    fixed_string_t& operator=( fixed_string_t&& ) = default;
    virtual ~fixed_string_t() = default;

};

// Helper - Wrapper Function used to create the templated type
template<size_t fixed_size>
fixed_string_t<fixed_size> fixed_string(  const char(&words)[fixed_size] ) {
    return fixed_string_t<fixed_size>( words );
}

#endif // FIXED_STRING_H

使用它看起来像这样:

#include <iostream>
#include "FixedString.h"

int main() {
    auto c = fixed_string( "hello" );
    std::cout << c.c_str() << " has a size of " c.size() << " with\n"
              << "a character count of " << c.count() << std::endl;
    return 0;
}

目前唯一的问题是该实体不可修改。字符串本身是固定的。这只是一个模式或演示,说明您正在寻找的类型的设计模式可能是什么。您可以添加或扩展它、借鉴它甚至完全忽略它。这是你的选择。