奇怪的对象分配行为c ++

时间:2017-06-06 16:15:21

标签: c++ class constructor visual-studio-2017 destructor

我对对象分配有一种奇怪的行为。如果你能解释为什么这个任务会像这样工作,我将非常感激。这花了我很多时间。 我正在使用Visual Studio Enterprise 2017(所有默认设置)。

代码:

def click():
entered_text = entry.get()
if len(entered_text) < 9:
    output.delete(0.0, END)
    output.insert(END, 'This is too short')     #Click function
elif entered_text in pass_Type:
    strength = pass_Type[entered_text]
    output.delete(0.0, END)
    output.insert(END, strength)
elif strength and len(entered_text) < 9: }
   output.delete(0.0, END)               }
   output.insert(END, strength)          } #What I entered
else:
    output.delete(0.0, END)
    output.insert(END, "This password is acceptable!") #When the password is ok

输出(最高为cin):

#include "stdafx.h"
#include <iostream>

using namespace std;

class Test
{
public:

    Test()
    {
        cout << "Constructor of " << this << endl;
    }

    ~Test()
    {
        cout << "Destructor of " << this << endl;
    }
};


int main()
{
    cout << "Assignment 1" << endl;
    auto t = Test();
    cout << "Assignment 2" << endl;
    t = Test();

    int i = 0;
    cin >> i;
    return 0;
}

预期输出(最高为cin):

Assignment 1
Constructor of 006FFC9F
Assignment 2
Constructor of 006FFBC7
Destructor of 006FFBC7

我想编写一个测试函数,它创建我的(模板)类的对象,进行一些测试,然后创建一个新对象并进行更多测试。问题是t在第二次赋值后保存已经被破坏的对象。 我知道我可以使用动态分配导致预期的行为,但为什么这个程序表现不同?

非常感谢你。 问候。

PS:结果是相同的,与Release / Debug或64/32位编译无关

编辑:更详细的例子:

Assignment 1
Constructor of 006FFC9F
Assignment 2
Destructor of 006FFC9F
Constructor of 006FFBC7

输出(它在末尾保存一个删除的指针!!!):

#include "stdafx.h"
#include <iostream>

using namespace std;

class Test
{
private:
    float* val;
public:
    Test()
    {
        val = new float;
        cout << "Constructor of " << this << ", addr. of val: " << val << endl;
    }

    ~Test()
    {
        cout << "Destructor of " << this << ", addr. of val: " << val << " --> DELETING VAL!" << endl;
        delete val;
    }

    float* getVal() { return this->val; }
};


int main()
{
    cout << "Assignment 1" << endl;
    auto t = Test();
    cout << "Assignment 2" << endl;
    t = Test();
    cout << "Val Address: " << t.getVal() << endl;

    int i = 0;
    cin >> i;
    return 0;
}

2 个答案:

答案 0 :(得分:3)

使用

auto t = Test();

您实际上构建了两个对象。首先是构造临时对象的Test()。第二个是t的构造,它是通过 copy-construction 制作的。这里没有任何作业,即使使用=运算符,它也是复制构造。

如果你向Test类添加一个类似于你的构造函数和析构函数的拷贝构造函数,你应该清楚地看到它。

至于

t = Test();

此处使用Test()创建临时对象。然后将该临时对象传递给Test类的(编译器生成的)赋值运算符,然后立即销毁临时对象。

对象t本身不会被破坏,它不应该是分配的目的地。

答案 1 :(得分:1)

您的混淆似乎是错误的期望,即在分配时原始对象被销毁。比如,在这段代码中:

cout << "Assignment 2" << endl;
t = Test();

这段代码调用move-assign运算符。既然你没有定义一个,那么编译器生成的默认值或多或少看起来像这样:

Test & operator=(Test &&) {}

请注意,该代码中没有构造函数或(严格地)析构函数的调用。将要运行的唯一构造函数和析构函数位于临时对象上(这是您在实际输出中观察到的)。在代码超出范围之前,原始对象不会被销毁;为什么会这样?在此之前你不能停止使用堆栈空间。

编辑:可能有助于您了解正在发生的事情:

#include<iostream>

struct Test {
    Test() {std::cout << "Constructed.\n";}
    ~Test() {std::cout << "Destructed.\n";}
    Test(Test const&) {std::cout << "Copy-Constructed.\n";}
    Test(Test &&) {std::cout << "Move-Constructed.\n";}
    Test & operator=(Test const&) {std::cout << "Copy-Assigned.\n"; return *this;}
    Test & operator=(Test &&) {std::cout << "Move-Assigned.\n"; return *this;}
};

int main() {
    std::cout << "Test t;\n";
    Test t; //Construction
    std::cout << "Test t2(t);\n";
    Test t2(t); //Copy-Construct
    std::cout << "Test t3(std::move(t2));\n";
    Test t3(std::move(t2)); //Move-Construct
    std::cout << "Test t4 = t;\n";
    Test t4 = t; //Copy Construct, due to Copy Ellision
    std::cout << "Test t5 = Test();\n";
    Test t5 = Test(); //Will probably be a normal Construct, due to Copy Ellision
    std::cout << "t = t2;\n";
    t = t2; //Copy Assign
    std::cout << "t = Test();\n";
    t = Test(); //Move Assign, will invoke Constructor and Destructor on temporary
    std::cout << "Done! Cleanup will now happen!\n";
    return 0;
}

compiled here时的结果:

Test t;
Constructed.
Test t2(t);
Copy-Constructed.
Test t3(std::move(t2));
Move-Constructed.
Test t4 = t;
Copy-Constructed.
Test t5 = Test();
Constructed.
t = t2;
Copy-Assigned.
t = Test();
Constructed.
Move-Assigned.
Destructed.
Done! Cleanup will now happen!
Destructed.
Destructed.
Destructed.
Destructed.
Destructed.

DOUBLE EDIT COMBO!

正如我在评论中提到的,val只是一个指针。 8个字节(在64位计算机上)作为Test存储的一部分分配。如果您尝试确保Test始终包含尚未删除的val的有效值,则需要实施Rule of Five(以前称为class Test { float * val; public: Test() {val = new float;} ~Test() {delete val; Test(Test const& t) { val = new float(*(t.val)); } Test(Test && t) {std::swap(val, t.val);} Test & operator=(Test const& t) { float * temp = new float(*(t.val)); //Gives Strong Exception Guarantee delete val; val = temp; return *this; } Test & operator=(Test && t) {std::swap(val, t.val); return *this;}; float & get_val() const {return *val;} //Return by reference, not by pointer, to //prevent accidental deletion. }; 三条规则):

#include <tuple>
#include <typeinfo>

template <typename F>
struct make_tuple_of_params;

template <typename Ret, typename... Args>
struct make_tuple_of_params<Ret (Args...)>
{
   using type = std::tuple<Args...>;
};

template <typename F>
using make_tuple_of_params_t = typename make_tuple_of_params<F>::type;

template<typename F>
void some_magic_function(F f)
{
   // if F is in the form void(double*, double*)
   // make_tuple_of_params is std::tuple<double*, double*>
   make_tuple_of_params_t<F> params;

   // ...
}

void Foo(double* x, double* y) { }

int main()
{
   some_magic_function(Foo);
}