我经常看到,当 C++ 函数想要返回数据缓冲区时,调用者必须提供指向缓冲区第一个元素的指针和计数作为函数参数。有时这是有意义的,因为函数的返回值用于传达函数的结果。但是我们不能只使用像 std::vector 或 std::array 这样的 STL 容器在函数中创建数据集合并返回指向该数据的指针吗?
如果我们考虑CNG API中的
BcryptGenRandom()
方法,
NTSTATUS BCryptGenRandom(
[in, out] BCRYPT_ALG_HANDLE hAlgorithm,
[in, out] PUCHAR pbBuffer,
[in] ULONG cbBuffer,
[in] ULONG dwFlags
);
我可以将此函数与“现代 C++”功能一起使用:
//Method 1
#include <iostream>
#include <cstdint>
#include <memory>
#include <array>
#include <exception>
#include <Windows.h>
#include <bcrypt.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
template <ULONG N>
std::shared_ptr<std::array<BYTE, N>> GenRandom()
{
std::shared_ptr<std::array<BYTE, N>> pBuffer{ new std::array<BYTE, N> };
if (!NT_SUCCESS(BCryptGenRandom(NULL, pBuffer->data(), pBuffer->size(), BCRYPT_USE_SYSTEM_PREFERRED_RNG)))
{
throw std::exception();
}
return pBuffer;
}
int main() {
const int size{ 64 };
std::shared_ptr<std::array<BYTE, size>> arr{};
try
{
arr = GenRandom<size>();
}
catch (const std::exception& ex)
{
return 1;
}
for (int i{ 0 }; i < arr->size(); i++)
{
std::cout << std::hex << static_cast<std::uint16_t>((*arr)[i]);
}
return 0;
}
或者以大多数文档和教程中出现的老式方式:
//Method 2
int main() {
const int size{ 64 };
BYTE buffer[size]{};
if (!NT_SUCCESS(BCryptGenRandom(NULL, buffer, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG)))
{
return 1;
}
for (int i{ 0 }; i < size; i++)
{
std::cout << std::hex << static_cast<std::int16_t>(buffer[i]);
}
return 0;
}
尽管第二种方法看起来更简单、更直接,但我总觉得第一种方法提供了更好的封装和错误处理机制,同时智能指针负责释放动态分配的内存(例如:当我要使用这个
GenRandom()
方法可以在代码库的不同位置生成任意大小的随机值)。
那么,在使用缓冲区时,与第二种方法相比,第一种方法在性能、兼容性或我所遗漏的方面是否有任何重大缺点?
智能指针的目的是取代手动内存管理(
new
/delete
,或malloc
/free
)。
如果这是一个可行的选择来写
BYTE buffer[size]{};
...那么使用智能指针(尤其是
std::shared_ptr
)完全没有意义,除了浪费性能之外什么也没有。编译器已经自动为您管理内存,将其包装在智能指针中并不会让这段代码变得更好。
std::array
std::array<BYTE, size> buffer{};
另请参阅:std::array 与数组性能