固定大小字符串的最佳实践 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;
// ...
};
所有访问器(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&>?
设计选择有利也有弊,但无论选择哪一种,每个功能的实现也要非常简洁。
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;
}
目前唯一的问题是该实体不可修改。字符串本身是固定的。这只是一个模式或演示,说明您正在寻找的类型的设计模式可能是什么。您可以添加或扩展它、借鉴它甚至完全忽略它。这是你的选择。
我想要一个固定大小的字符串 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 thestd::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;
// ...
};
所有访问器(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&>?
设计选择有利也有弊,但无论选择哪一种,每个功能的实现也要非常简洁。
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 thebasic_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;
}
目前唯一的问题是该实体不可修改。字符串本身是固定的。这只是一个模式或演示,说明您正在寻找的类型的设计模式可能是什么。您可以添加或扩展它、借鉴它甚至完全忽略它。这是你的选择。