我想要一个固定大小的字符串类。理想情况下,接口将匹配std::string
的接口与新类永远不会分配新内存的一个区别。对于应该避免分配新内存的应用程序情况,它应该是一个方便的类。大小可以是静态的(在编译时已知)。
我认为有两种方法。第一个是围绕char
数组实现一个类,然后或多或少地实现std::string
所具有的所有函数。我还必须实现一些运算符来创建具有给定固定大小字符串等的std::string
。
第二种方法,我甚至不确定是否可能,是继承std::string and
覆盖所有可能改变字符串大小的函数。我查看了Visual Studio中的basic_string
标题,它似乎不是虚拟的,所以我想这不是可行的方法。
您认为实施此类课程的最佳方法是什么?
答案 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;
// ...
};
所有访问者(data
,c_str
,begin
,end
,at
,operator[]
)都是单行的。所有搜索算法都很简单。
唯一真正的设计问题是你希望突变在失败时做什么。那就是:
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;
}
目前唯一的一点就是这个实体不可修改。字符串本身是固定的。这只是一种模式或演示您正在寻找的类型的设计模式。您可以添加或扩展它,借用它甚至完全忽略它。这是你的选择。