C++ 防止析构函数在C++;

C++ 防止析构函数在C++;,c++,C++,我希望确保对象的析构函数不会运行。除了将对象放在堆上而不调用delete之外,还有其他方法可以做到这一点吗?如果您希望在调用析构函数时发生错误,那么就不要为其提供定义: struct foo { ~foo(); }; 或者在C++11中删除它: struct foo { ~foo() = delete; }; 如果你只是希望它有时被调用,那么你几乎肯定需要重新考虑你的设计。没有“安全”的方法可以做到这一点,虽然使用new而不删除可能有效,但我强烈建议您不要这样做 或者,如果有时需要某

我希望确保对象的析构函数不会运行。除了将对象放在堆上而不调用delete之外,还有其他方法可以做到这一点吗?

如果您希望在调用析构函数时发生错误,那么就不要为其提供定义:

struct foo {
  ~foo();
};
或者在C++11中删除它:

struct foo {
  ~foo() = delete;
};
如果你只是希望它有时被调用,那么你几乎肯定需要重新考虑你的设计。没有“安全”的方法可以做到这一点,虽然使用
new
而不删除可能有效,但我强烈建议您不要这样做

或者,如果有时需要某些析构函数行为,可以添加一个标志:

struct foo {
  foo(bool destroy = true) : destroy(destroy) {}
  ~foo() {
    if(destroy) {
      // destruction stuff here
    }
  }
  bool destroy;
};

另一种方法是放置新的操作符。

,但还有另一种方法:

char buffer[sizeof(T) + alignof(T)];
char* aligned_buffer = buffer + alignof(T) - reinterpret_cast<intptr_t>(buffer) % alignof(T);
T* object = new (aligned_buffer) T;
当然,堆分配也是如此:

delete object;
要防止出现这种情况,必须使析构函数不可访问:

struct T
{
private:
   ~T() {};
};
或者确实无法访问(相关:):

按照操作方式,您可以创建自己的更简单的对象包装器模板:

#include <iostream>

template <typename T_>
struct Wrapper
{
    union {
        char dummy_;
        T_ value_;
    };
    bool shouldDestroy_ {true};

    template<typename ...Args>
    Wrapper(Args &&... args) :
        value_(std::forward<Args>(args)...)
    {
    }

    const T_ &value() const { return value_; }
    T_ &value() { return value_; }

    const T_ *get() const { return &value_; }
    T_ *get() { return &value_; }

    const T_ *operator->() const { return get(); }
    T_ *operator->() { return get(); }

    void destroy(bool destroy) { shouldDestroy_ = destroy; }

    ~Wrapper()
    {
        if (shouldDestroy_) {
            value_.~T_();
        }
    }

};

int main()
{
    struct Foo
    {
        Foo() { std::cout << "Foo()\n"; }
        Foo(int value) { std::cout << "Foo(" << value << ")\n"; }
        ~Foo() { std::cout << "~Foo()\n"; }

        void display(const char *str) { std::cout << str << "\n"; }
    };

    std::cout << "Running...\n";

    // example 1 construct/destructed    
    {
        Wrapper<Foo> a;
        a.value().display("via value 1");
        a->display("via operator 1");
    }

    // example 2 construct/destructed    
    {
        Wrapper<Foo> a(2);
        a.value().display("via value 2");
        a->display("via operator 2");
    }

    // example 3 construct NOT destructed    
    {
        Wrapper<Foo> a(3);
        a.value().display("via value 3");
        a->display("via operator 3");

        // do not allow destroy
        a.destroy(false);
    }

    return 0;
}

<>我很惊讶,没有人提到完全符合C++标准的解决方案,它有<代码>联合< /COD>。在
union
中,不会为成员自动调用构造函数甚至析构函数。即使在工会只有一个成员的情况下。所有这些都必须“手动”完成:

  • 构造函数可以通过所谓的“placement new”(或从
    引入的构造函数初始化列表)调用

  • 析构函数可以通过显式调用析构函数方法来调用

演示:


您可以看到没有
1已销毁
也没有
101已销毁
。我猜像std::Optional这样的类型也是以类似的方式实现的。

不调用特定析构函数的最佳方法是不创建该对象的实例。如果不行,就把你不想从析构函数中用完的代码拿走。我想不出有什么理由这样做是合法的。而且,你提到了唯一的方法。太多的戴维。事实上,使用堆不是唯一的方法,但它是最简单的。你想解决什么问题,让你相信这是最好的解决方案?@Dave:噢!如果你只是想让它活到节目结束,有很多好方法可以做到这一点@Loki:vector不会回答“除了将对象放在堆上,还有其他方法吗?”,因为vector的内容又回到了堆上。OMG,有人写了这个静态缓冲区破解,并正确地对齐了对象。真不敢相信发生了这种事+1.@R.MartinhoFernandes:Oh ye of little faith.@Loki:无论如何,我看不到任何保证向量内容对于元素类型以外的任何类型都是适当对齐的。@Loki:我熟悉这个要求。但是我看不出它是如何应用于
std::vector
数据的。对于前两个示例,
foo
仍然需要在堆上,如果您想使用
foo
@Jesse:请参阅我的答案,以获得在不使用堆的情况下实例化此类的方法。但是不要这样做!我一定是忽略了@Robin R的答案——但无论如何,还是把它保留在这里。
class indestructible_base
{
    ~indestructible_base();
};

struct T : indestructible_base
{
};
#include <iostream>

template <typename T_>
struct Wrapper
{
    union {
        char dummy_;
        T_ value_;
    };
    bool shouldDestroy_ {true};

    template<typename ...Args>
    Wrapper(Args &&... args) :
        value_(std::forward<Args>(args)...)
    {
    }

    const T_ &value() const { return value_; }
    T_ &value() { return value_; }

    const T_ *get() const { return &value_; }
    T_ *get() { return &value_; }

    const T_ *operator->() const { return get(); }
    T_ *operator->() { return get(); }

    void destroy(bool destroy) { shouldDestroy_ = destroy; }

    ~Wrapper()
    {
        if (shouldDestroy_) {
            value_.~T_();
        }
    }

};

int main()
{
    struct Foo
    {
        Foo() { std::cout << "Foo()\n"; }
        Foo(int value) { std::cout << "Foo(" << value << ")\n"; }
        ~Foo() { std::cout << "~Foo()\n"; }

        void display(const char *str) { std::cout << str << "\n"; }
    };

    std::cout << "Running...\n";

    // example 1 construct/destructed    
    {
        Wrapper<Foo> a;
        a.value().display("via value 1");
        a->display("via operator 1");
    }

    // example 2 construct/destructed    
    {
        Wrapper<Foo> a(2);
        a.value().display("via value 2");
        a->display("via operator 2");
    }

    // example 3 construct NOT destructed    
    {
        Wrapper<Foo> a(3);
        a.value().display("via value 3");
        a->display("via operator 3");

        // do not allow destroy
        a.destroy(false);
    }

    return 0;
}
Running...
Foo()
via value 1
via operator 1
~Foo()
Foo(2)
via value 2
via operator 2
~Foo()
Foo(3)
via value 3
via operator 3
class MyObj {
  int i;
public:
  MyObj(int i_) : i(i_) { std::cout << i << " constructed" << std::endl; }
  MyObj(MyObj const &src) : i(src.i + 100) { std::cout << src.i << " copied to new " << i << std::endl; }
  ~MyObj() { std::cout << i << " destroyed" << std::endl; }
};

class OptDestr {
  bool callDestructor;
  union { MyObj o; };  // Only allocated, but no constr/destr is called automatically

public:
  // Constructor
  OptDestr(int i, bool callDestructor_) : callDestructor(callDestructor_), 
    o(i) // calls MyObj constructor
  { }
  // OR alternatively:
  OptDestr(int i, bool callDestructor_) : callDestructor(callDestructor_) {
    new (&o)MyObj(i);  // placement new - does NOT allocate, just calls constructor
  }

  // Copy constructor
  OptDestr(OptDestr const &src) : callDestructor(src.callDestructor),
    o(src.o)  // explicit call of MyObj copy-constructor
  { }
  // OR alternatively:
  OptDestr(OptDestr const &src) : callDestructor(src.callDestructor) {
    new (&o)MyObj(src.o);  // placement new - no allocation, just explicitly calls copy-constructor of MyObj
  }

  // Destructor
  ~OptDestr() {
    if (callDestructor) o.~MyObj();  // explicit call of destructor
  }
};


int main() {
  OptDestr d1(1, false /*callDestructor*/);
  OptDestr d1_copy(d1);
  OptDestr d2(2, true /*callDestructor*/);
  OptDestr d2_copy(d2);
}
1 constructed
1 copied to new 101
2 constructed
2 copied to new 102
102 destroyed
2 destroyed