Some organizations and entities (including the White House) have advised against using C/C++ and use memory safe languages with memory safe features instead. While I can understand the motivation for such encouragement, realistically complete abandonment of the language isn’t practical. Managing low-level resources in other languages can both be cumbersome and doesn’t necessarily insulate someone from resource leaks. There are not always higher-level libraries available for functionality that one wishes to use; they may have to build a library themselves and embrace management of those low level resources. But that said, when writing code in C++, one can use safer approaches to doing so. One approach is to use std::shared_ptr<T> instead of using pointers directly.
Shared pointers implement reference counters and will delete the underlying memory once that reference count reaches zero. This is a feature that is often common in some other high level languages. Instead of using the new and delete commands to allocate and release memory, one could use std::make_shared. For other blocks of data for which you might have manually allocated memory, you could use other standard template library classes, such as using a std::vector instead of an array.
Sometimes a resource in question was allocated by the operating system. It may be up to the developer to manage the release or deletion of the object. These can still be managed with std::shared_ptrs<T> objects. Let’s take a look at a simple program that reads a program into a buffer,
#include <iostream>
#include <Windows.h>
const DWORD64 MAX_FILE_SIZE = 64 * 1024;//64 kilobytes
int main(int argc, char** argv)
{
if (argc < 2)
{
std::wcout << L"Usage: ShowFileContents <filename>" << std::endl;
return 1;
}
std::string filename = argv[1];
std::wstring wfilename = std::wstring(filename.begin(), filename.end());
HANDLE hFile = CreateFile(wfilename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
DWORD fileSizeHigh, fileSizeLow;
DWORD64 fileSize = -1;
DWORD bytesRead = -1;
fileSizeLow = GetFileSize(hFile, &fileSizeHigh);
fileSize = ((DWORD64)fileSizeHigh << 32) + fileSizeLow;
if (fileSize > MAX_FILE_SIZE)
{
std::wcout << L"File is too big to read" << std::endl;
CloseHandle(hFile);
return 1;
}
std::wcout << L"File size: " << fileSize << std::endl;
char* buffer = new char[fileSize + 1];
ZeroMemory(buffer, fileSize + 1);
ReadFile(hFile, buffer, fileSize, &bytesRead, NULL);
std::wcout << L"File contents: " << std::endl;
std::wcout << buffer << std::endl;
delete buffer;
CloseHandle(hFile);
return 0;
}
There first thing I see that can be replaced is a call to the new and delete that could be removed. I’ll replace the use of this buffer with a vector<T>. Since I am using a vector, I don’t need to explicitly allocate and deallocate memory. Instead, I can specify how much memory is needed in its declaration. When the std::vector falls out of scope, it will be deallocated automatically. I do make use of a pointer to the vector’s memory. It is accessible through the method std::vector<T>::data(). The ReadFile method needs a pointer to the memory in which it will deposit its data. That’s provided by way of this method.
There is also a HANDLE variable used for managing the file. It is named hFile. I’ve written on wrapping these in unique pointers before. You can read about that here. In that post, I implemented a Functor that contains the definition for how the handle is to be deleted. Rather than manually ensure I associate the functor with the shared pointer, I had also made a function that would handle that for me to ensure it is done the same way every time. This can also be used with a std::shared_ptr<T>. Though you should generally only do this if you really need to share the resource with more than one object. On a unique pointer, the deleter is part of the object type. On a shared pointer, the deleter is not part of the type, but is stored in instance data for the pointer. I’ll replace my usage of CreateFile (the Win32 function) with wrapper function that returns the handle as a std::shared_ptr. That wrapper function looks like this.
using HANDLE_shared_ptr = std::shared_ptr<void>;
HANDLE_shared_ptr CreateFileHandle(
std::wstring fileName,
DWORD dwDesiredAccess = GENERIC_READ,
DWORD dwShareMode = FILE_SHARE_READ,
LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL,
DWORD dwCreationDisposition = OPEN_EXISTING,
DWORD dwFlagsAndAttributes = 0,
HANDLE hTemplateFile = NULL)
{
//std::shared_ptr<HANDLE> x = nullptr;
HANDLE handle = CreateFile(fileName.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
if (handle == INVALID_HANDLE_VALUE || handle == nullptr)
{
return nullptr;
}
return std::shared_ptr<void>(handle, HANDLECloser());
}
In the following, you can see the new implementation of my main() method. Notice that in the ReadFile method for the std::shared_ptr<T> that I am calling its get() method to pass the HANDLE value to the function. I’m nolonger explicitly invoking the call to CloseHandle(). Instead, when the main() method returns, the deleter will be invoked indirectly. If you set a breakpoint on it you’ll see when this happens.
int main(int argc, char** argv)
{
DWORD fileSizeHigh, fileSizeLow;
DWORD64 fileSize = -1;
DWORD bytesRead = -1;
if (argc < 2)
{
std::wcout << L"Usage: ShowFileContents <filename>" << std::endl;
return 1;
}
std::string filename = argv[1];
std::wstring wfilename = std::wstring(filename.begin(), filename.end());
auto fileHandle = CreateFileHandle (wfilename.c_str());
fileSizeLow = GetFileSize(fileHandle.get(), &fileSizeHigh);
fileSize = ((DWORD64)fileSizeHigh << 32) + fileSizeLow;
if (fileSize > MAX_FILE_SIZE)
{
std::wcout << L"File is too big to read" << std::endl;
return 1;
}
std::wcout << L"File size: " << fileSize << std::endl;
std::vector<char> buffer(fileSize / sizeof(char) + 1, 0);
ReadFile(fileHandle.get(), buffer.data(), fileSize, &bytesRead, NULL);
std::string bufferText = std::string(buffer.begin(), buffer.end());
std::wcout << L"File contents: " << std::endl;
std::cout << bufferText << std::endl;
return 0;
}
You’ll see use of this soon in an upcoming post on SmartCards. The code examples for it make Windows API calls to the Smart Card functions. I’ll be making use of shared pointers with deleters for managing the resources in that project.
Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.
Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet


