2017-05-14 4 views
3

Consider J'ai une classe sans constructeur public, mais une usine ou une méthode statique de constructeur, et la taille de l'objet créé dépend des arguments passés à l'usine (ou constructeur).C++ 11 pointeur intelligent créer des fonctions pour les objets de taille variable

Existe-t-il un moyen de créer un pointeur partagé (ou unique) vers un tel objet en utilisant std::make_shared (ou std::make_unique)?

Pour tout polymorphisme suggéré, je préfère des modèles sur des méthodes virtuelles.

+1

Que voulez-vous dire par la « taille » de l'objet? Vous ne pouvez pas avoir des instances de la même classe qui diffèrent en taille. – Quentin

+0

« Size » est la longueur en octets de la mémoire contiguë qui est alloué lorsqu'un objet est construit, et libéré quand elle est détruite. –

+0

Pourriez-vous éditer dans un code montrant comment vous créez ces objets * sans * pointeurs intelligents? – Quentin

Répondre

3

Tenez compte que j'ai une classe sans constructeur public ...

Y at-il un moyen de créer un pointeur partagé (ou unique) à un tel objet à l'aide std::make_shared (ou std::make_unique)?

Il n'y a pas de telle manière. std::make_shared et std::make_unique nécessitent un constructeur public.

Au lieu de cela, vous pouvez créer le pointeur partagé (unique) sans la commodité "faire" fonction:

// within the factory 
return std::shared_ptr<T>(new T(size)); 

PS. La bibliothèque standard C++ 11 n'a pas de std::make_unique.

-1
#include <memory> 
#include <vector> 
#include <iostream> 

template <typename T> 
std::unique_ptr<T> buildObjectWithSize(std::size_t size) { 
    return std::unique_ptr<T>{T::buildObject(size)}; 
} 

class MyObject { 
public: 
    static MyObject* buildObject(std::size_t size) { 
     return new MyObject(size); 
    } 

    int& operator[](int i) { 
     return int_vector[i]; 
    } 
private: 
    MyObject(std::size_t size) 
    : int_vector(size) { 
    } 

    std::vector<int> int_vector; 
}; 

int main() { 
    constexpr unsigned INT_VECTOR_SIZE{3U}; 

    std::unique_ptr<MyObject> my_object_up{buildObjectWithSize<MyObject>(INT_VECTOR_SIZE)}; 

    (*my_object_up.get())[1] = 5; 
    std::cout << (*my_object_up.get())[1] << '\n'; 

    MyObject &my_object_ref = *my_object_up.get(); 
    my_object_ref[2] = 3; 
    std::cout << my_object_ref[2] << '\n'; 
} 
2

Si vous avez une méthode d'usine en place qui construira l'objet dans un bloc de mémoire que vous fournissez, plus une méthode qui vous indique la quantité d'espace dont vous aurez besoin, vous pouvez le faire.

Je vais utiliser librement C++ 14, ajouter vos propres typename et ::type si vous avez vraiment besoin de C++ 11.

D'abord, nous supposons que nous avons ceci:

struct some_args { 
    // whatever 
}; 
std::size_t how_big_is_X(some_args); 
X* make_X(some_args, void* buffer); 

avec ce qui précède, je peux faire quelque chose fonctionnellement équivalent à make_shared.

template<std::size_t Sz, std::size_t align=alignof(void*)> 
struct smart_buffer_t { 
    void(*dtor)(void*) = 0; 
    std::aligned_storage_t< Sz, align> data; 
    template<class T, class...Args> 
    T* emplace(Args&&...args) { 
    return ctor([&](void* pdata) { 
     ::new(pdata) T(std::forward<Args>(args)...); 
    }); 
    } 
    template<class F> 
    T* ctor(F&& f) { 
    std::forward<F>(f)((void*)&data); 
    dtor = [](void* ptr){ 
     static_cast<T*>(ptr)->~T(); 
    }; 
    return static_cast<T*>((void*)&data); 
    } 
    ~smart_buffer_t() { 
    if (dtor) dtor(&data); 
    } 
}; 

template<class T, std::size_t Sz, std::size_t Algn=alignof(void*), class F> 
std::shared_ptr<T> 
make_sized(F&& f) { 
    auto pbuff = std::make_shared<smart_buffer_t<Sz, Algn>>(); 
    T* r = pbuff->ctor(std::forward<F>(f)); 
    return {r, pbuff}; // aliasing ctor 
} 

nous avons maintenant:

template<std::size_t I> 
using index_t = std::integral_constant<std::size_t, I>; 
template<std::size_t I> 
using pow_t = index_t< (1<<I) >; 

std::shared_ptr<X> 
make_shared_X(some_args args) { 
    std::size_t Sz = how_big_is_X(args); 
    using pmaker = std::shared_ptr<X>(*)(some_args); 
    using maker_maker = [](auto Sz){ 
    return +[](some_args args) { 
     return make_sized<X, Sz>([&](void* ptr){ 
     return make_X(args, ptr); 
     }); 
    }; 
    }; 
    static const pmaker table[] = { 
    maker_maker(pow_t<0>{}), 
    maker_maker(pow_t<1>{}), 
    maker_maker(pow_t<2>{}), 
    // ... 
    maker_maker(pow_t<63>{}), // assuming 64 bit size_t. 
    }; 
    std::size_t i = 0; 
    while(Sz > (1<<i)) 
    ++i; 
    return table[i](args); 
} 

ou somesuch.

Code non testé. Il alloue la puissance de 2 à ou plus que la demande de votre args. Mais l'objet est construit dans la même allocation que le bloc de comptage de référence.

Toute série au lieu de puissances de 2 peut être utilisée, mais la taille de la table doit être suffisamment grande pour gérer la plus grande valeur de retour possible de how_big_is_X.