Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 何时调用复制构造函数和析构函数,为什么?_C++_Destructor_Copy Constructor - Fatal编程技术网

C++ 何时调用复制构造函数和析构函数,为什么?

C++ 何时调用复制构造函数和析构函数,为什么?,c++,destructor,copy-constructor,C++,Destructor,Copy Constructor,代码是: #include <iostream> class P_Node { friend class Picture; protected: P_Node() : use(1) {} virtual ~P_Node() {} private: int use; }; class Picture { friend Picture frame(const Picture&); public: Picture() : p(ne

代码是:

#include <iostream>

class P_Node {
    friend class Picture;
protected:
    P_Node() : use(1) {}
    virtual ~P_Node() {}
private:
    int use;
};

class Picture {
    friend Picture frame(const Picture&);
public:
    Picture() : p(new P_Node) {
        std::cout << "Constructor\t" << "Picture::Picture()" << "\tcalled" << std::endl;
        std::cout << "Picture p count\t" << p->use << std::endl;
    }
    Picture(const Picture& orig) : p(orig.p) {
        std::cout << "Copy Constructor\t" << "Picture::Picture(const Picture&)" << "\tcalled" << std::endl;
        std::cout << "Picture p count\t" << p->use << std::endl;
        orig.p->use++;
    }
    ~Picture() {
        std::cout << "Destructor\t" << "Picture::~Picture()" << "\tcalled" << std::endl;
        std::cout << "Picture p count before decrease\t" << p->use << std::endl;
        if(--p->use == 0) {
            std::cout << "Picture p count after decrease\t" << p->use << std::endl;
            std::cout << "Deleted" << std::endl;
            delete p;
        }
    }
    Picture& operator=(const Picture& orig) {
        std::cout << "operator=\t" << "Picture& Picture::operator=(const Picture& orig)" << "\tcalled" << std::endl;
        std::cout << "Picture p count before decrease\t" << p->use << std::endl;
        orig.p->use++;
        if(--p->use == 0) {
            std::cout << "Picture p count after decrease\t" << p->use << std::endl;
            std::cout << "Deleted" << std::endl;
            delete p;
        }
        p = orig.p;
        return *this;
    }
private:
    Picture(P_Node* p_node) : p(p_node) {
        std::cout << "Picture::Picture(P_Node* p_node)\tcalled" << std::endl;
    }
    P_Node *p;
};

class Frame_Pic : public P_Node {
    friend Picture frame(const Picture&);
private:
    Frame_Pic(const Picture& pic) : p(pic) {
        std::cout << "Frame_Pic::Frame_Pic(const Picture& orig)" << "\tcalled" << std::endl;
    }
    Picture p;
};

Picture frame(const Picture& pic) {
    return new Frame_Pic(pic);
}

int main() {
    Picture my_pic;
    Picture temp = frame(my_pic);
    return 0;
}
#包括
类P_节点{
朋友班图片;
受保护的:
P_Node():使用(1){}
虚拟~P_节点(){}
私人:
int使用;
};
班级照片{
朋友相框(const Picture&);
公众:
Picture():p(新的p_节点){

std::cout在您认为可能或应该调用复制构造函数时,不一定会调用它:

以下情况可能导致调用复制构造函数:

  • 当对象按值返回时
  • 当对象以值作为参数传递(给函数)时
  • 当抛出一个对象时
  • 当捕捉到物体时
  • 将对象放置在大括号内的初始值设定项列表中时
  • 这些情况统称为复制初始化,相当于:
    tx=a

    但是,, 不保证在这些情况下会调用副本构造函数, 因为C++标准允许编译器优化拷贝 在某些情况下,一个例子是 (有时称为RVO)


    堆栈上的任何内容的析构函数在超出范围时都会被调用。

    复制构造函数不一定会在您认为可能或应该调用时被调用:

    以下情况可能导致调用复制构造函数:

  • 当对象按值返回时
  • 当对象以值作为参数传递(给函数)时
  • 当抛出一个对象时
  • 当捕捉到物体时
  • 将对象放置在大括号内的初始值设定项列表中时
  • 这些情况统称为复制初始化,相当于:
    tx=a

    但是,, 不保证在这些情况下会调用副本构造函数, 因为C++标准允许编译器优化拷贝 在某些情况下,一个例子是 (有时称为RVO)


    堆栈上任何内容的析构函数在超出范围时被调用。

    回答您的问题

  • 在语句
    Picture temp=frame(my_pic);
    之后不会调用copy构造函数,因为没有任何语句会导致该语句之后出现任何副本

  • 调用
    Picture
    的三个析构函数(按顺序):
    temp
    p
    temp.p
    my_Pic
    指向的
    帧Pic
    中进行销毁。编译器已避免生成任何其他临时
    Picture
    对象

  • 是的,可以调用复制构造函数来初始化
    图片帧(const Picture&pic)
    的返回值,但允许编译器(在这种情况下确实如此)消除复制并直接从返回表达式初始化返回值

  • 是的,如果将
    frame
    的参数更改为按值传递,则可能会生成额外的复制构造函数调用,但如果该参数是用一个表达式初始化的,而该表达式不是引用现有对象的glvalue,则参数可能会直接用该表达式初始化,并且该副本会被省略

  • 每当类类型的对象被实际复制时,就会调用复制构造函数。这可能是在传递给函数或从函数返回时调用的,但在这些情况下,编译器有时可以忽略不必要的副本

  • 是的,每当类类型的对象被销毁时都会调用析构函数。对于编译器生成的命名变量和临时变量来说,这是正确的。可以在不调用析构函数的情况下结束对象的生命周期,例如,我将其内存重新用于另一个对象,但这是一个非常特殊的情况


  • 回答你的问题

  • 在语句
    Picture temp=frame(my_pic);
    之后不会调用copy构造函数,因为没有任何语句会导致该语句之后出现任何副本

  • 调用
    Picture
    的三个析构函数(按顺序):
    temp
    p
    temp.p
    my_Pic
    指向的
    帧Pic
    中进行销毁。编译器已避免生成任何其他临时
    Picture
    对象

  • 是的,可以调用复制构造函数来初始化
    图片帧(const Picture&pic)
    的返回值,但允许编译器(在这种情况下确实如此)消除复制并直接从返回表达式初始化返回值

  • 是的,如果将
    frame
    的参数更改为按值传递,则可能会生成额外的复制构造函数调用,但如果该参数是用一个表达式初始化的,而该表达式不是引用现有对象的glvalue,则参数可能会直接用该表达式初始化,并且该副本会被省略

  • 每当类类型的对象被实际复制时,就会调用复制构造函数。这可能是在传递给函数或从函数返回时调用的,但在这些情况下,编译器有时可以忽略不必要的副本

  • 是的,每当类类型的对象被销毁时都会调用析构函数。对于编译器生成的命名变量和临时变量来说,这是正确的。可以在不调用析构函数的情况下结束对象的生命周期,例如,我将其内存重新用于另一个对象,但这是一个非常特殊的情况


  • 注意:在所有说明将调用复制构造函数的答案中,可能不会调用复制构造函数,因为编译器进行了一些优化

    1) 为什么不在
    Picture temp=frame(my_pic)
    之后调用copy constructor

    Picture temp=frame(my_pic);是return语句之前的最后一行,因此所有这些都会发生 Constructor Picture::Picture() called Picture p count 1 Copy Constructor Picture::Picture(const Picture&) called Picture p count 1 Frame_Pic::Frame_Pic(const Picture& orig) called Picture::Picture(P_Node* p_node) called Destructor Picture::~Picture() called Picture p count before decrease 1 Picture p count after decrease 0 Deleted Destructor Picture::~Picture() called Picture p count before decrease 2 Destructor Picture::~Picture() called Picture p count before decrease 1 Picture p count after decrease 0 Deleted