new
operator is not very efficient in allocating memory of small size, and secondly, the frequent allocation of small memory blocks in itself requires a lot of resources. #include <iostream> #include <memory> struct Base { static std::unique_ptr<Base> create(bool x); virtual void f() const = 0; virtual ~Base() { std::cout << "~Base()" << std::endl;} }; struct A: public Base { A() {std::cout << "A()" << std::endl;} virtual void f() const override {std::cout << "A::f\t" << ((size_t)this) << std::endl;} virtual ~A() {std::cout << "~A()" << std::endl;} }; struct B: public Base { B() {std::cout << "B()" << std::endl;} virtual void f() const override {std::cout << "B::f\t" << ((size_t)this) << std::endl;} virtual ~B() {std::cout << "~B()" << std::endl;} }; std::unique_ptr<Base> Base::create(bool x) { if(x) return std::unique_ptr<Base>(new A()); else return std::unique_ptr<Base>(new B()); } int main() { auto p = Base::create(true); p->f(); std::cout << "p addr:\t" << ((size_t)&p) << std::endl; return 0; } // compile & run: // g++ -std=c++11 1.cpp && ./a.out
A() A::f 21336080 p addr: 140733537175632 ~A() ~Base()
#include <iostream> #include <memory> struct Base { virtual void f() const = 0; virtual ~Base() { std::cout << "~Base()" << std::endl;} }; struct A: public Base {/* code here */}; struct B: public Base {/* code here */}; class BaseCreator { union U { A a; B b; }; public: BaseCreator(bool x) : _x(x) { if(x) (new(m) A()); else (new(m) B()); } ~BaseCreator() { if(_x) { reinterpret_cast<A*>(m)->A::~A(); } else { reinterpret_cast<B*>(m)->B::~B(); } } Base* operator->() { return reinterpret_cast<Base *>(m); } private: bool _x; unsigned char m[sizeof(U)]; }; int main(int argc, char const *argv[]) { BaseCreator p(true); p->f(); std::cout << "p addr:\t" << ((size_t)&p) << std::endl; return 0; }
A() A::f 140735807769160 p addr: 140735807769160 ~A() ~Base()
unsigned char m[sizeof(U)];
and with the help of the special form new
place the object new(m) A()
.reinterpret_cast<A*>(m)->A::~A();
correctly destroys the object placed in the allocated memory. class BaseCreator { union U { A a; B b; }; public: BaseCreator(bool x) { if(x) createObj<A>(); else createObj<B>(); } ~BaseCreator() { deleter(m); } // BaseCreator(const BaseCreator &) = delete; // BaseCreator(BaseCreator &&) = default; Base* operator->() { return reinterpret_cast<Base *>(m); } private: typedef void (deleter_t)(void *); template<typename T> void createObj() { new(m) T(); deleter = freeObj<T>; } template<typename T> static void freeObj(void *p) { reinterpret_cast<T*>(p)->T::~T(); } unsigned char m[sizeof(U)]; deleter_t *deleter; };
template <typename ...Types> class TypeUnion { public: // TypeUnion() {}; // TypeUnion(const TypeUnion &) = delete; // TypeUnion(TypeUnion &&) = default; ~TypeUnion() { // - // , if(deleter) deleter(mem); } // "" T // T // args template <typename T, typename ...Args> void assign(Args&&... args) { // "" static_assert ( usize, "TypeUnion is empty" ); static_assert ( same_as<T>(), "Type must be present in the types list " ); // - // , . if(deleter) deleter(mem); // // , new(mem) T(std::forward<Args>(args)...); // deleter = freeMem<T>; } // "" template<typename T> T* get() { static_assert ( usize, "TypeUnion is empty" ); assert ( deleter ); // TypeUnion::assign was not called return reinterpret_cast<T*>(mem); } private: // typedef void (deleter_t)(void *); // TypeUnion ? static constexpr size_t max() { return 0; } // static constexpr size_t max(size_t r0) { return r0; } template <typename ...R> static constexpr size_t max(size_t r0, R... r) { return ( r0 > max(r...) ? r0 : max(r...) ); } // is_same template <typename T> static constexpr bool same_as() { return max( std::is_same<T, Types>::value... ); } // template<typename T> static void freeMem(void *p) { reinterpret_cast<T*>(p)->T::~T(); } // static constexpr size_t usize = max( sizeof(Types)... ); // , unsigned char mem[usize]; deleter_t *deleter = nullptr; };
class BaseCreator { TypeUnion<A, B> obj; public: BaseCreator(bool x) { if(x) obj.assign<A>(); else obj.assign<B>(); } // BaseCreator(const BaseCreator &) = delete; // BaseCreator(BaseCreator &&) = default; Base* operator->() { return obj.get<Base>(); } };
TypeUnion<A, B> obj
clearer than union U {A a; B b;}
union U {A a; B b;}
. And an error with type mismatch will be caught at compile time. #include <iostream> #include <memory> #include <cassert> struct Base { virtual void f() const = 0; virtual ~Base() {std::cout << "~Base()\n";} }; struct A: public Base { A(){std::cout << "A()\n";} virtual void f() const override{std::cout << "A::f\n";} virtual ~A() {std::cout << "~A()\n";} }; struct B: public Base { B(){std::cout << "B()\n";} virtual void f() const override{std::cout << "B::f\n";} virtual ~B() {std::cout << "~B()\n";} size_t i = 0; }; template <typename ...Types> class TypeUnion { public: // TypeUnion() {}; // TypeUnion(const TypeUnion &) = delete; // TypeUnion(TypeUnion &&) = default; ~TypeUnion() { // - // , if(deleter) deleter(mem); } // "" T // T // args template <typename T, typename ...Args> void assign(Args&&... args) { // "" static_assert ( usize, "TypeUnion is empty" ); static_assert ( same_as<T>(), "Type must be present in the types list " ); // - // , . if(deleter) deleter(mem); // // , new(mem) T(std::forward<Args>(args)...); // deleter = freeMem<T>; } // "" template<typename T> T* get() { static_assert ( usize, "TypeUnion is empty" ); assert ( deleter ); // TypeUnion::assign was not called return reinterpret_cast<T*>(mem); } private: // typedef void (deleter_t)(void *); // TypeUnion ? static constexpr size_t max() { return 0; } // static constexpr size_t max(size_t r0) { return r0; } template <typename ...R> static constexpr size_t max(size_t r0, R... r) { return ( r0 > max(r...) ? r0 : max(r...) ); } // is_same template <typename T> static constexpr bool same_as() { return max( std::is_same<T, Types>::value... ); } // template<typename T> static void freeMem(void *p) { reinterpret_cast<T*>(p)->T::~T(); } // static constexpr size_t usize = max( sizeof(Types)... ); // , unsigned char mem[usize]; deleter_t *deleter = nullptr; }; class BaseCreator { TypeUnion<A, B> obj; public: BaseCreator(bool x) { if(x) obj.assign<A>(); else obj.assign<B>(); } // BaseCreator(const BaseCreator &) = delete; // BaseCreator(BaseCreator &&) = default; Base* operator->() { return obj.get<Base>(); } }; int main(int argc, char const *argv[]) { BaseCreator p(false); p->f(); std::cout << "sizeof(BaseCreator):" << sizeof(BaseCreator) << std::endl; std::cout << "sizeof(A):" << sizeof(A) << std::endl; std::cout << "sizeof(B):" << sizeof(B) << std::endl; return 0; } // // clang++ -std=c++11 1.cpp && ./a.out
Source: https://habr.com/ru/post/244497/
All Articles