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

时间:2016-08-26 17:36:42

标签: c++ size fixed stdstring string-length

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

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

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

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

2 个答案:

答案 0 :(得分:12)

  

第一个是围绕char数组实现一个类,然后实现std::string所具有的所有函数。

这绝对是要走的路。它易于编写,易于使用且难以滥用。

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

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

    // 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&>?

设计选择有优点和缺点,但不管你选择哪一个,每个功能的实现也将非常简洁。

  

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

std::string中的任何内容virtual是否与{1}}无关,这与这是一个好主意的问题无关。你肯定想从:

开始
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继承将会带来更复杂,效率更低的实施,而不仅仅是直接路线,还有更多潜在的陷阱。实现这样的事情可能可能,但我看不出它是一个好主意。

答案 1 :(得分:-1)

我继续前进,制作了一个可以构建的简单课程。结构如下:基类是一个仅声明的接口,它只包含你想要拥有的构造函数类型的签名,以及一个必须在继承类中实现的所有函数的列表 - 类,因为它们是纯粹的虚拟。派生类是具有实际实现的模板类。您不能直接使用该类,因为有一个辅助函数模板,它为您想要支持的每个构造函数类型获取要传递的类型,并返回该类型。

#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] = '\0';
    }

    // 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;
}

目前唯一的一点就是这个实体不可修改。字符串本身是固定的。这只是一种模式或演示您正在寻找的类型的设计模式。您可以添加或扩展它,借用它甚至完全忽略它。这是你的选择。