很多人使用字符串时,会直接使用标准库提供的string,但是string使用的是堆内存,无论申请还是释放还是造成内存碎片,都会对性能有比较大的影响.所以如果能使用栈内存来代替会有较大的性能提升.
template<int Length>
template<int Length>
class StackString
{
public:
// 不使用StackString() = default; 因为这样仍然会允许使用初始化列表,但是使用初始化列表可能会引起歧义导致容易出现错误
StackString() {}
// 为了方便查代码,将返回指针的函数命名为具体的名字
const char* str() const { return mValue; }
// 仅为了在类型匹配时能够编译通过,尽量避免直接使用
const char* data() const { return mValue; }
char* data() { return mValue; }
bool isEmpty() const { return mValue[0] == '\0'; }
void clear() { mValue[0] = '\0'; }
int length() const
{
for (int i = 0; i < Length; ++i)
{
if (mValue[i] == '\0')
{
return i;
}
}
return Length;
}
constexpr int size() const { return Length; }
constexpr const char& operator[](int index) const
{
if (index < 0 || index >= Length)
{
cout << "数组越界" << endl;
index = Length - 1;
}
return mValue[index];
}
constexpr char& operator[](int index)
{
if (index < 0 || index >= Length)
{
cout << "数组越界" << endl;
index = Length - 1;
}
return mValue[index];
}
public:
char mValue[Length]{};
};
template<int Length>
class StackString
{
public:
// 不使用StackString() = default; 因为这样仍然会允许使用初始化列表,但是使用初始化列表可能会引起歧义导致容易出现错误
StackString() {}
// 为了方便查代码,将返回指针的函数命名为具体的名字
const char* str() const { return mValue; }
// 仅为了在类型匹配时能够编译通过,尽量避免直接使用
const char* data() const { return mValue; }
char* data() { return mValue; }
bool isEmpty() const { return mValue[0] == '\0'; }
void clear() { mValue[0] = '\0'; }
int length() const
{
for (int i = 0; i < Length; ++i)
{
if (mValue[i] == '\0')
{
return i;
}
}
return Length;
}
constexpr int size() const { return Length; }
constexpr const char& operator[](int index) const
{
if (index < 0 || index >= Length)
{
cout << "数组越界" << endl;
index = Length - 1;
}
return mValue[index];
}
constexpr char& operator[](int index)
{
if (index < 0 || index >= Length)
{
cout << "数组越界" << endl;
index = Length - 1;
}
return mValue[index];
}
public:
char mValue[Length]{};
};
大多数使用字符串的时候都是已经知晓字符串最大的长度为多少,所以就可以使用栈内存的字符串来代替string.
可以根据自己的需求封装函数,使其达到string的易用性.
以下是测试代码:
#include <chrono>
#include <iostream>
#include <string>
using namespace std;
template<int Length>
class StackString
{
public:
// 不使用StackString() = default; 因为这样仍然会允许使用初始化列表,但是使用初始化列表可能会引起歧义导致容易出现错误
StackString() {}
// 为了方便查代码,将返回指针的函数命名为具体的名字
const char* str() const { return mValue; }
// 仅为了在类型匹配时能够编译通过,尽量避免直接使用
const char* data() const { return mValue; }
char* data() { return mValue; }
bool isEmpty() const { return mValue[0] == '\0'; }
void clear() { mValue[0] = '\0'; }
int length() const
{
for (int i = 0; i < Length; ++i)
{
if (mValue[i] == '\0')
{
return i;
}
}
return Length;
}
constexpr int size() const { return Length; }
constexpr const char& operator[](int index) const
{
if (index < 0 || index >= Length)
{
cout << "数组越界" << endl;
index = Length - 1;
}
return mValue[index];
}
constexpr char& operator[](int index)
{
if (index < 0 || index >= Length)
{
cout << "数组越界" << endl;
index = Length - 1;
}
return mValue[index];
}
public:
char mValue[Length]{};
};
int TEST_COUNT = 100000000;
void test_std_string()
{
using namespace chrono;
auto start = high_resolution_clock::now();
string s;
for (int i = 0; i < TEST_COUNT; ++i)
{
s = "123";
}
auto end = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(end - start);
cout << "[std::string] Time: " << duration.count() << " ms " << "str:" << s.data() << "\n";
}
void test_stack_string()
{
using namespace chrono;
auto start = high_resolution_clock::now();
StackString<32> s;
for (int i = 0; i < TEST_COUNT; ++i)
{
memcpy(s.data(), "123", 4);
}
auto end = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(end - start);
cout << "[StackString] Time: " << duration.count() << " ms " << "str:" << s.data() << "\n";
system("pause");
}
int main()
{
cout << "===== 性能基准测试 =====" << "\n";
cout << "测试次数: " << TEST_COUNT << " 次\n\n";
cout << "-- std::string 测试 --" << "\n";
test_std_string();
cout << "\n-- StackString 测试 --" << "\n";
test_stack_string();
return 0;
}
最终测试结果: