c – 通过指向非多态类型的基类获取已分配内存的地址

前端之家收集整理的这篇文章主要介绍了c – 通过指向非多态类型的基类获取已分配内存的地址前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
简单的多继承
  1. struct A {};
  2. struct B {};
  3. struct C : A,B {};

或虚拟继承

  1. struct B {};
  2. struct C : virtual B {};

请注意类型不是多态的.

自定义内存分配:

  1. template <typedef T,typename... Args>
  2. T* custom_new(Args&& args...)
  3. {
  4. void* ptr = custom_malloc(sizeof(T));
  5. return new(ptr) T(std::forward<Args>(args)...);
  6. }
  7.  
  8. template <typedef T>
  9. void custom_delete(T* obj)
  10. {
  11. if (!obj)
  12. return obj;
  13.  
  14. void* ptr = get_allocated_ptr(obj); // here
  15. assert(std::is_polymorphic_v<T> || ptr == obj);
  16. obj->~T();
  17. custom_free(ptr); // heap corruption if assert ^^ Failed
  18. }
  19.  
  20. B* b = custom_new<C>(); // b != address of allocated memory
  21. custom_delete(b); // UB

如何为非多态类型实现get_allocated_ptr?对于多态类型,dynamic_cast< void *>做这个工作

或者,我可以检查obj是指向基类的指针,因为通过指向基类的指针是UB来删除非多态对象.我不知道如何做到这一点,或者是可能的.

在这种情况下(例如VC),运算符删除正确地释放内存,尽管标准是UB.怎么做到这一点?编译器专用功能

解决方法

你实际上比获取完整对象的地址有一个更严重的问题.考虑这个例子:
  1. struct Base
  2. {
  3. std::string a;
  4. };
  5.  
  6. struct Derived : Base
  7. {
  8. std::string b;
  9. };
  10.  
  11. Base* p = custom_new<Derived>();
  12. custom_delete(p);

在这个例子中,custom_delete实际上会释放正确的地址(static_cast< void *>(static_cast< Derived *>(p))== static_cast< void *>(p)),但是行obj- T()将调用Base的析构函数,这意味着b字段被泄露.

所以不要这样做

不要从custom_new返回一个原始指针,而是返回绑定到T类型的对象,并知道如何删除它.例如:

  1. template <class T> struct CustomDeleter
  2. {
  3. void operator()(T* object) const
  4. {
  5. object->~T();
  6. custom_free(object);
  7. }
  8. };
  9.  
  10. template <typename T> using CustomPtr = std::unique_ptr<T,CustomDeleter<T>>;
  11.  
  12. template <typename T,typename... Args> CustomPtr<T> custom_new(Args&&... args)
  13. {
  14. void* ptr = custom_malloc(sizeof(T));
  15. try
  16. {
  17. return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
  18. }
  19. catch (...)
  20. {
  21. custom_free(ptr);
  22. throw;
  23. }
  24. }

现在不可能不小心释放错误的地址并调用错误的析构函数,因为调用custom_free的唯一代码知道它正在删除的事物的完整类型.

注意:请注意unique_ptr :: reset(pointer)方法.这种方法在使用自定义删除器时非常危险,因为onus是在调用者上提供以正确方式分配的指针.如果使用无效指针调用方法,编译器将无法帮助.

通过基指针

可能是你想要将一个基指针传递给一个函数,并赋予该函数释放对象的责任.在这种情况下,您需要使用类型擦除来从消费者隐藏对象的类型,同时在内部保留其最常派生类型的知识.最简单的方法是使用std :: shared_ptr.例如:

  1. struct Base
  2. {
  3. int a;
  4. };
  5.  
  6. struct Derived : Base
  7. {
  8. int b;
  9. };
  10.  
  11. CustomPtr<Derived> unique_derived = custom_new<Derived>();
  12.  
  13. 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创建对象.

完整的工作实例

  1. #include <memory>
  2. #include <iostream>
  3.  
  4. void* custom_malloc(size_t size)
  5. {
  6. void* mem = ::operator new(size);
  7. std::cout << "allocated object at " << mem << std::endl;
  8. return mem;
  9. }
  10.  
  11. void custom_free(void* mem)
  12. {
  13. std::cout << "freeing memory at " << mem << std::endl;
  14. ::operator delete(mem);
  15. }
  16.  
  17. template <class T> struct CustomDeleter
  18. {
  19. void operator()(T* object) const
  20. {
  21. object->~T();
  22. custom_free(object);
  23. }
  24. };
  25.  
  26. template <typename T> using CustomPtr = std::unique_ptr<T,typename... Args> CustomPtr<T> custom_new(Args&&... args)
  27. {
  28. void* ptr = custom_malloc(sizeof(T));
  29. try
  30. {
  31. return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
  32. }
  33. catch (...)
  34. {
  35. custom_free(ptr);
  36. throw;
  37. }
  38. }
  39.  
  40. struct Base
  41. {
  42. int a;
  43. ~Base()
  44. {
  45. std::cout << "destroying Base" << std::endl;
  46. }
  47. };
  48.  
  49. struct Derived : Base
  50. {
  51. int b;
  52. ~Derived()
  53. {
  54. std::cout << "detroying Derived" << std::endl;
  55. }
  56. };
  57.  
  58. int main()
  59. {
  60. // Since custom_new has returned a unique_ptr with a deleter bound to the
  61. // type Derived,we cannot accidentally free the wrong thing.
  62. CustomPtr<Derived> unique_derived = custom_new<Derived>();
  63.  
  64. // If we want to get a pointer to the base class while retaining the ability
  65. // to correctly delete the object,we can use type erasure. std::shared_ptr
  66. // will do the trick,but it's easy enough to write a similar class without
  67. // the sharing semantics.
  68. std::shared_ptr<Base> shared_base = std::shared_ptr<Derived>{ std::move(unique_derived) };
  69.  
  70. // Notice that when we release the shared_base pointer,we destroy the complete
  71. // object.
  72. shared_base.reset();
  73. }

猜你在找的C&C++相关文章