Table of Contents
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <iostream>
#include <string>
#include <exception>
#include <memory>
using FilePtr = std::unique_ptr<FILE, int (*)(FILE *)>;
constexpr void assert_that(bool statement, const char *msg) {
if (!statement) {
throw std::runtime_error(msg);
}
}
int main(int argc, char *argv[]) {
assert_that(argc == 2, "Usage: command [path]");
FILE *f = nullptr;
f = fopen(argv[1], "r+");
assert_that(f, strerror(errno));
// assign FILE* to a unique_ptr
FilePtr fptr{f, fclose};
assert_that(!!fptr, strerror(errno));
assert_that(fseek(fptr.get(), 0, SEEK_END) == 0, strerror(errno));
long size = ftell(fptr.get());
assert_that(size >=0, strerror(errno));
rewind(fptr.get());
// using unique_ptr to create a buffer instead of using malloc
std::unique_ptr<char[]> buf{ new char[size + 1]{0} };
assert_that(!!buf, strerror(errno));
size_t r = fread(buf.get(), 1, size, fptr.get());
assert_that(r == size, "Reading error");
std::cout << buf.get();
end:
return 0;
}
std::make_shared
and std::make_unique
are the recommended ways to
create smart pointers because compilers do guarantee the order of executions,
which may introduce memory leaks when an exception is thrown. For example, the
compilers may call new T
, then raise()
, and so on before foo
is
called. In this case, std::unique_ptr
does not know the pointer T
yet,
so it is still on the heap.
using uptr = std::unique_ptr<T>;
bool raise() {
throw std::exception();
return true;
}
foo(uptr(new T), raise(), uptr(new T));