删除类的数组而不调用析构函数

时间:2016-04-14 21:43:07

标签: c++ memory destructor

考虑我们使用这种方式创建数组:

T* arr = new T[num];

现在由于某些原因我们理解我们只需删除该数组但不调用任何T析构函数。

我们都知道,如果我们写下一个:

delete arr;

将调用T析构函数。 如果我们写这个:

delete[] arr;

将调用num析构函数。 使用指针后,您意识到new在结果指针之前插入了表示已分配unsigned long long实例数的T值。因此,我们尝试智取C++尝试将该值更改为arr占用的字节数,并将其删除为(char*),希望在这种情况下delete不会调用T实例的析构函数,只需释放占用的内存。所以你写这样的东西:

typedef unsigned long long;
unsll & num = *(unsll)((char*)arr-sizeof(unsll));
num = num*sizeof(T);
delete ((char*)arr);

但是这并不起作用,C++在尝试删除时会创建触发断点(运行时错误)。所以这不起作用。许多其他玩指针的工作并不起作用,因为至少会出现一些错误(编译或运行时)。所以问题是:

可以在不调用析构函数的情况下删除C ++中的类数组吗?

3 个答案:

答案 0 :(得分:2)

也许你想要.owl-item { margin-top: 70px; }

(见http://en.cppreference.com/w/cpp/memory/new/operator_delete

但是这仍然有不明确的行为,这是一个糟糕的主意。

答案 1 :(得分:1)

在不调用析构函数的情况下解除分配的一种简单方法是分离和初始化。在适当注意对齐时,可以使用放置new(或标准分配器对象的功能)在已分配的块内创建对象实例。然后在最后,您可以使用适当的释放函数来释放块。

我无法想到任何可以做到这一事情的情况:它强烈反映过早优化和X / Y问题(通过想象不切实际的Y作为解决方案处理问题X,然后询问只有Y)。

new - 表达式旨在将分配与初始化相结合,以便它们作为全有或全无操作执行。这种耦合以及用于清理和解除分配的同上耦合是正确性的关键,并且它还简化了很多事情(即,内部的复杂性是人们不必处理的)。解耦需要有一个很好的理由。避免析构函数调用,例如优化的目的,不是一个好理由。

答案 2 :(得分:0)

我只会解决你的具体问题

  

是否可以在不调用析构函数的情况下删除C ++中的类数组?

简短回答是

很长的答案是肯定的,但有一些警告并特别考虑析构函数是 for (即资源清理),避免调用类析构函数通常是一个坏主意。

在我继续回答之前,应该注意的是,这是专门回答你的问题,如果你正在使用C ++(而不是直接C),使用这个代码将起作用(因为它符合要求),但如果你'需要以这种方式生成代码,您可能需要重新考虑一些设计,因为这样的代码如果使用不当会导致错误/错误和一般的未定义行为。

TL; DR 如果你需要避免析构函数,你需要重新考虑你的设计(即使用复制/移动语义或STL容器)。

您可以使用mallocfree来避免构造函数和析构函数调用,例如代码:

#include <iostream>
#include <cstdio>

class MyClass {
    public:
        MyClass() : m_val(0)
        {
            this->init(42);
            std::cout << "ctor" << std::endl;
        }

        ~MyClass()
        {
            std::cout << "dtor" << std::endl;
        }

        friend std::ostream& operator<<(std::ostream& stream, const MyClass& val)
        {
            stream << val.m_val;
            return stream;
        }

        void init(int val)
        {
            /* just showing that the this pointer is valid and can
            reference private members regardless of new or malloc */
            this->_init(val);
        }

    private:
        int m_val;

        void _init(int val)
        {
            this->m_val = val;
        }   
};

template < typename Iterator >
void print(Iterator begin, Iterator end)
{
    while (begin != end) {
        std::cout << *begin << std::endl;
        ++begin;
    }
}

void set(MyClass* arr, std::size_t count)
{
    for (; count > 0; --count) {
        arr[count-1].init(count);
    }
}

int main(int argc, char* argv[])
{
    std::cout << "Calling new[10], 10 ctors called" << std::endl;
    MyClass* arr = new MyClass[10]; // 10 ctors called;
    std::cout << "0: " << *arr << std::endl;
    set(arr, 10);
    print(arr, arr+10);
    std::cout << "0: " << *arr << std::endl;
    std::cout << "Calling delete[], 10 dtors called" << std::endl;
    delete[] arr; // 10 dtors called;

    std::cout << "Calling malloc(sizeof*10), 0 ctors called" << std::endl;
    arr = static_cast<MyClass*>(std::malloc(sizeof(MyClass)*10)); // no ctors
    std::cout << "0: " << *arr << std::endl;
    set(arr, 10);
    print(arr, arr+10);
    std::cout << "0: " << *arr << std::endl;
    std::cout << "Calling free(), 0 dtors called" << std::endl;
    free(arr); // no dtors

    return 0;
}

应该注意的是,将newfree和/或mallocdelete混合会产生未定义的行为,因此调用{{ 1}}然后调用MyClass* arr = new MyClass[10];可能无法正常工作(因此是UB)。

在C ++中不调用构造函数/析构函数会产生的另一个问题是继承。上面的代码将与free(arr);malloc一起用于基本类,但是如果你开始抛出更复杂的类型,或者从其他类继承,那么继承类的构造函数/析构函数将不会被调用事情变得很丑陋,例如:

free

上面的代码是兼容的并且在#include <iostream> #include <cstdio> class Base { public: Base() : m_val(42) { std::cout << "Base()" << std::endl; } virtual ~Base() { std::cout << "~Base" << std::endl; } friend std::ostream& operator<<(std::ostream& stream, const Base& val) { stream << val.m_val; return stream; } protected: Base(int val) : m_val(val) { std::cout << "Base(" << val << ")" << std::endl; } void _init(int val) { this->m_val = val; } int m_val; }; class Child : public virtual Base { public: Child() : Base(42) { std::cout << "Child()" << std::endl; } ~Child() { std::cout << "~Child" << std::endl; } void init(int val) { this->_init(val); } }; template < typename Iterator > void print(Iterator begin, Iterator end) { while (begin != end) { std::cout << *begin << std::endl; ++begin; } } void set(Child* arr, std::size_t count) { for (; count > 0; --count) { arr[count-1].init(count); } } int main(int argc, char* argv[]) { std::cout << "Calling new[10], 20 ctors called" << std::endl; Child* arr = new Child[10]; // 20 ctors called; // will print the first element because of Base::operator<< std::cout << "0: " << *arr << std::endl; set(arr, 10); print(arr, arr+10); std::cout << "0: " << *arr << std::endl; std::cout << "Calling delete[], 20 dtors called" << std::endl; delete[] arr; // 20 dtors called; std::cout << "Calling malloc(sizeof*10), 0 ctors called" << std::endl; arr = static_cast<Child*>(std::malloc(sizeof(Child)*10)); // no ctors std::cout << "The next line will seg-fault" << std::endl; // Segfault because the base pointers were never initialized std::cout << "0: " << *arr << std::endl; // segfault set(arr, 10); print(arr, arr+10); std::cout << "0: " << *arr << std::endl; std::cout << "Calling free(), 0 dtors called" << std::endl; free(arr); // no dtors return 0; } 和Visual Studio上编译没有错误,但是由于继承,当我尝试在g++之后打印第一个元素时都崩溃(因为基类从未初始化。)

因此,您确实可以在不调用构造函数和析构函数的情况下创建和删除对象数组,但这样做会导致您需要注意的一些额外场景,并考虑到以避免未定义的行为或崩溃,如果这样做您的代码就是这种情况,因此您需要确保不调用析构函数,您可能需要重新考虑整体设计(甚至可能使用STL容器或智能指针类型)。

希望可以提供帮助。