samedi 26 août 2023

How to prevent creating new instance with the default initialization or the new keyword? [duplicate]

My goal is to prevent initializing FibonacciMemoization class except with the FibonacciMemoization::getInstance() function.

Goal:

  • [ ] prevent initializing with FibonacciMemoization* object1 = new FibonacciMemoization();
  • [ ] prevent initializing with FibonacciMemoization object1;

The code

// https://en.cppreference.com/w/cpp/memory/weak_ptr
// https://refactoring.guru/design-patterns/singleton/cpp/example#example-1

#ifndef FIBONACCI_MEMOIZATION_HPP
#define FIBONACCI_MEMOIZATION_HPP

#include <unordered_map>
#include <mutex>

class FibonacciMemoization {
public:
    static std::shared_ptr<FibonacciMemoization> getInstance();

    /**
     * Singleton should not be cloneable.
     * 
     * auto object1 = FibonacciMemoization(); // no matchng constructor for initialization of 'FibonacciMemoization'
     * This line creates an instance of the `FibonacciMemoization` class using its constructor and then assigns this newly created instance to the `object1`.
     * This involves both construction and assignment.
     * 
     * FibonacciMemoization object1;
     * FibonacciMemoization object2 = object1; // function "FibonacciMemoization::FibonacciMemoization(FibonacciMemoization &other)" cannot be referenced -- it is a deleted function
     * This line does not create a new instance of the `FibonacciMemoization` class. 
     * Instead, it creates a new variable `object2` and attempts to initialize it by copying the value of `object1`.
     * Without deleting the copy constructor `object2` would be a separate instance that could behave independently of `object1`.
     * 
     * auto object1 = FibonacciMemoization::getInstance();
     * FibonacciMemoization object2 = *object1; // function "FibonacciMemoization::FibonacciMemoization(FibonacciMemoization &other)" cannot be referenced -- it is a deleted function
     * `object2` would be a separate instance that could behave independently of `object1`.
     */
    FibonacciMemoization(FibonacciMemoization &other) = delete;

    /** 
     * Singleton should not be assignable.
     * auto object1 = FibonacciMemoization();
     * auto object2 = FibonacciMemoization();
     * object2 = object1; // function "FibonacciMemoization::operator=(const FibonacciMemoization &)" cannot be referenced - it is a deleted function
     */
    void operator=(const FibonacciMemoization &) = delete;

    ~FibonacciMemoization();

    unsigned long calculate(unsigned n);
private:
    static std::weak_ptr<FibonacciMemoization> instance;
    static std::mutex mutex;

    FibonacciMemoization();

    int id;
    std::unordered_map<unsigned, unsigned> cache;

    unsigned long fibonacci(unsigned n);
};

#endif

#include "fibonacciMemoization.hpp"
#include <iostream>

FibonacciMemoization::FibonacciMemoization() {
    srand(time(0));
    id = rand();
    std::cout << typeid(*this).name() << " " << __func__ << " " << id << "\n\n";
}

FibonacciMemoization::~FibonacciMemoization() {
    std::cout << typeid(*this).name() << " " << __func__ << " " << id << "\n\n";
}

std::weak_ptr<FibonacciMemoization> FibonacciMemoization::instance;
std::mutex FibonacciMemoization::mutex;

std::shared_ptr<FibonacciMemoization> FibonacciMemoization::getInstance() {
    std::shared_ptr<FibonacciMemoization> sp;

    std::lock_guard<std::mutex> lock(mutex);
    if (instance.expired()) {
        sp = std::make_shared<FibonacciMemoization>();
        instance = sp;
    }

    return instance.lock();
}

unsigned long FibonacciMemoization::calculate(unsigned n) {
    cache.clear();
    return fibonacci(n);
}

unsigned long FibonacciMemoization::fibonacci(unsigned n) {
    if (n < 2)
        return n;

    if (cache.find(n) != cache.end())
        return cache[n];

    unsigned long fib_n = fibonacci(n - 1) + fibonacci(n - 2);
    cache[n] = fib_n;

    return fib_n;
}

The usage code

#include <chrono>
#include <iostream>

#include "fibonacci.cpp"
#include "fibonacciMemoization.hpp"

void fibonacciExample() {
    const auto start = std::chrono::steady_clock::now();
    const auto fb = fibonacci(42);
    const auto end = std::chrono::steady_clock::now();
    const std::chrono::duration<double> elapsed_seconds = end - start;
 
    std::cout << "fibonacci example\n";
    std::cout << "f(42) = " << fb << '\n' << "elapsed time: ";
    std::cout << elapsed_seconds.count() << "s\n\n"; // Before C++20
// std::cout << elapsed_seconds << "\n\n"; // C++20: operator<< chrono::duration
}

void fibonacciMemoizationExample() {
    const auto start = std::chrono::steady_clock::now();

    // example A pf the unexpected usage.
    // FibonacciMemoization object;
    // const auto fb = object.calculate(42);

// example B of the unexpected usage.
// auto object = new FibonacciMemoization();
// const auto fb = object->calculate(42);
    
// the expected usage
auto object = FibonacciMemoization::getInstance();
const auto fb = object->calculate(42);

    const auto end = std::chrono::steady_clock::now();
    const std::chrono::duration<double> elapsed_seconds = end - start;
 
    std::cout << "fibonacci memoization example\n";
    std::cout << "f(42) = " << fb << '\n' << "elapsed time: ";
    std::cout << elapsed_seconds.count() << "s\n\n";

// example B of the unexpected usage.
// need to explicitly call destructor after use due to use of the `new` keyword.
// delete object;
}

The problem is that std::make_shared<FIbonacciMemoization>() require public constructor and destructor.

What I've tried:

  1. set FibonacciMemoization constructor to private.

The expected result: the compiler and run do not generate error.

The actual result: the compiler generate error.

g++ -std=c++11 main.cpp fibonacciMemoization.cpp -o main && ./main
In file included from main.cpp:1:
In file included from ./menu.cpp:3:
./fibonacciexample.cpp:23:26: error: calling a private constructor of class 'FibonacciMemoization'
    FibonacciMemoization object;
                         ^
./fibonacciMemoization.hpp:48:5: note: declared private here
    FibonacciMemoization();
    ^
1 error generated.
In file included from fibonacciMemoization.cpp:1:
In file included from ./fibonacciMemoization.hpp:7:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/unordered_map:523:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__hash_table:25:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/memory:860:
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/shared_ptr.h:294:37: error: calling a private constructor of class 'FibonacciMemoization'
        ::new ((void*)__get_elem()) _Tp(_VSTD::forward<_Args>(__args)...);
                                    ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/shared_ptr.h:953:55: note: in instantiation of function template specialization 'std::__shared_ptr_emplace<FibonacciMemoization, std::allocator<FibonacciMemoization>>::__shared_ptr_emplace<>' requested here
    ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, _VSTD::forward<_Args>(__args)...);
                                                      ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/shared_ptr.h:962:19: note: in instantiation of function template specialization 'std::allocate_shared<FibonacciMemoization, std::allocator<FibonacciMemoization>, void>' requested here
    return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...);
                  ^
fibonacciMemoization.cpp:22:19: note: in instantiation of function template specialization 'std::make_shared<FibonacciMemoization, void>' requested here
        sp = std::make_shared<FibonacciMemoization>();
                  ^
./fibonacciMemoization.hpp:48:5: note: declared private here
    FibonacciMemoization();
    ^
1 error generated.

Aucun commentaire:

Enregistrer un commentaire