关于C++中Cross dll的new与delete的问题

在C++中一个dll与exe,若dll采用静态的crt,则在exe中通过调用dll的接口得到一个类的指针,使用完后在exe中采用delete析构,则运行时产生堆错误
例如如下代码中,exe通过dll的GetCrossDllClass接口获取一个指针,该指针所指对象在dll中new出来,而在exe中delete,则程序崩溃

#dll.h
class COMMON_API CrossDllClass
{
public:
    CrossDllClass();
    ~CrossDllClass();

    void SetData(int* data);
private:
    int* _data;
};

COMMON_API CrossDllClass* GetCrossDllClass();

#dll.cpp
CrossDllClass::CrossDllClass()
    :_data(new int[100])
{
    
}

CrossDllClass::~CrossDllClass()
{
    delete[] _data;
}

void CrossDllClass::SetData(int* data)
{
    delete[] _data;
    _data = data;
}

CrossDllClass* GetCrossDllClass()
{
    return new CrossDllClass;
}


#exe的main.cpp
int main(int argc, char** argv)
{
    CrossDllClass* p = GetCrossDllClass();
    delete p;
    return 0;
}

但相同的设置如果将CrossDllClass类的析构函数设置为虚函数,则不会运行时报错,原因是为什么呢;同时标准库的智能指针可以跨库【并且不怕采用不同crt库】的原因

#dll.h
class COMMON_API CrossDllClass
{
public:
    CrossDllClass();
    virtual ~CrossDllClass();

    void SetData(int* data);
private:
    int* _data;
};

COMMON_API CrossDllClass* GetCrossDllClass();

#dll.cpp
CrossDllClass::CrossDllClass()
    :_data(new int[100])
{
    
}

CrossDllClass::~CrossDllClass()
{
    delete[] _data;
}

void CrossDllClass::SetData(int* data)
{
    delete[] _data;
    _data = data;
}

CrossDllClass* GetCrossDllClass()
{
    return new CrossDllClass;
}


#exe的main.cpp
int main(int argc, char** argv)
{
    CrossDllClass* p = GetCrossDllClass();
    delete p;
    return 0;
}

C Run-Time Libraries https://msdn.microsoft.com/en-us/library/abx4dbyh.aspx