- struct A {};
- struct B {};
- struct C : A,B {};
或虚拟继承
- struct B {};
- struct C : virtual B {};
请注意类型不是多态的.
自定义内存分配:
- template <typedef T,typename... Args>
- T* custom_new(Args&& args...)
- {
- void* ptr = custom_malloc(sizeof(T));
- return new(ptr) T(std::forward<Args>(args)...);
- }
- template <typedef T>
- void custom_delete(T* obj)
- {
- if (!obj)
- return obj;
- void* ptr = get_allocated_ptr(obj); // here
- assert(std::is_polymorphic_v<T> || ptr == obj);
- obj->~T();
- custom_free(ptr); // heap corruption if assert ^^ Failed
- }
- B* b = custom_new<C>(); // b != address of allocated memory
- custom_delete(b); // UB
如何为非多态类型实现get_allocated_ptr?对于多态类型,dynamic_cast< void *>做这个工作
或者,我可以检查obj是指向基类的指针,因为通过指向基类的指针是UB来删除非多态对象.我不知道如何做到这一点,或者是可能的.
解决方法
- struct Base
- {
- std::string a;
- };
- struct Derived : Base
- {
- std::string b;
- };
- Base* p = custom_new<Derived>();
- custom_delete(p);
在这个例子中,custom_delete实际上会释放正确的地址(static_cast< void *>(static_cast< Derived *>(p))== static_cast< void *>(p)),但是行obj- T()将调用Base的析构函数,这意味着b字段被泄露.
所以不要这样做
不要从custom_new返回一个原始指针,而是返回绑定到T类型的对象,并知道如何删除它.例如:
- template <class T> struct CustomDeleter
- {
- void operator()(T* object) const
- {
- object->~T();
- custom_free(object);
- }
- };
- template <typename T> using CustomPtr = std::unique_ptr<T,CustomDeleter<T>>;
- template <typename T,typename... Args> CustomPtr<T> custom_new(Args&&... args)
- {
- void* ptr = custom_malloc(sizeof(T));
- try
- {
- return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
- }
- catch (...)
- {
- custom_free(ptr);
- throw;
- }
- }
现在不可能不小心释放错误的地址并调用错误的析构函数,因为调用custom_free的唯一代码知道它正在删除的事物的完整类型.
注意:请注意unique_ptr :: reset(pointer)方法.这种方法在使用自定义删除器时非常危险,因为onus是在调用者上提供以正确方式分配的指针.如果使用无效指针调用该方法,编译器将无法帮助.
通过基指针
可能是你想要将一个基指针传递给一个函数,并赋予该函数释放对象的责任.在这种情况下,您需要使用类型擦除来从消费者隐藏对象的类型,同时在内部保留其最常派生类型的知识.最简单的方法是使用std :: shared_ptr.例如:
- struct Base
- {
- int a;
- };
- struct Derived : Base
- {
- int b;
- };
- CustomPtr<Derived> unique_derived = custom_new<Derived>();
- std::shared_ptr<Base> shared_base = std::shared_ptr<Derived>{ std::move(unique_derived) };
现在,您可以自由传递shared_base,当最终引用发布时,完整的Derived对象将被销毁,其正确的地址传递给custom_free.如果你不喜欢shared_ptr的语义,那么创建一个带有unique_ptr语义的类型擦除指针是非常简单的.
注意:这种方法的一个缺点是,shared_ptr需要为其控件块单独分配(不会使用custom_malloc).再多一点工作,你可以解决这个问题.您需要创建一个包装custom_malloc和custom_free的自定义分配器,然后使用std :: allocate_shared创建对象.
完整的工作实例
- #include <memory>
- #include <iostream>
- void* custom_malloc(size_t size)
- {
- void* mem = ::operator new(size);
- std::cout << "allocated object at " << mem << std::endl;
- return mem;
- }
- void custom_free(void* mem)
- {
- std::cout << "freeing memory at " << mem << std::endl;
- ::operator delete(mem);
- }
- template <class T> struct CustomDeleter
- {
- void operator()(T* object) const
- {
- object->~T();
- custom_free(object);
- }
- };
- template <typename T> using CustomPtr = std::unique_ptr<T,typename... Args> CustomPtr<T> custom_new(Args&&... args)
- {
- void* ptr = custom_malloc(sizeof(T));
- try
- {
- return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
- }
- catch (...)
- {
- custom_free(ptr);
- throw;
- }
- }
- struct Base
- {
- int a;
- ~Base()
- {
- std::cout << "destroying Base" << std::endl;
- }
- };
- struct Derived : Base
- {
- int b;
- ~Derived()
- {
- std::cout << "detroying Derived" << std::endl;
- }
- };
- int main()
- {
- // Since custom_new has returned a unique_ptr with a deleter bound to the
- // type Derived,we cannot accidentally free the wrong thing.
- CustomPtr<Derived> unique_derived = custom_new<Derived>();
- // If we want to get a pointer to the base class while retaining the ability
- // to correctly delete the object,we can use type erasure. std::shared_ptr
- // will do the trick,but it's easy enough to write a similar class without
- // the sharing semantics.
- std::shared_ptr<Base> shared_base = std::shared_ptr<Derived>{ std::move(unique_derived) };
- // Notice that when we release the shared_base pointer,we destroy the complete
- // object.
- shared_base.reset();
- }