精华内容
下载资源
问答
  • 复制构造函数
    2022-02-13 09:12:08

    C++拷贝构造函数(复制构造函数)详解

    拷贝和复制是一个意思,对应的英文单词都是copy。对于计算机来说,拷贝是指用一份原有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的图片拷贝到桌面以方便浏览,将重要的文件上传到百度网盘以防止丢失等,都是「创建一份新数据」的意思。

    在 C++ 中,拷贝并没有脱离它本来的含义,只是将这个含义进行了“特化”,是指用已经存在的对象创建出一个新的对象。从本质上讲,对象也是一份数据,因为它会占用内存。

    严格来说,对象的创建包括两个阶段,首先要分配内存空间,然后再进行初始化:

    分配内存很好理解,就是在堆区、栈区或者全局数据区留出足够多的字节。这个时候的内存还比较“原始”,没有被“教化”,它所包含的数据一般是零值或者随机值,没有实际的意义。

    初始化就是首次对内存赋值,让它的数据有意义。注意是首次赋值,再次赋值不叫初始化。初始化的时候还可以为对象分配其他的资源(打开文件、连接网络、动态分配内存等),或者提前进行一些计算(根据价格和数量计算出总价、根据长度和宽度计算出矩形的面积等)等。说白了,初始化就是调用构造函数。

    很明显,这里所说的拷贝是在初始化阶段进行的,也就是用其它对象的数据来初始化新对象的内存。

    那么,如何用拷贝的方式来初始化一个对象呢?其实这样的例子比比皆是,string 类就是一个典型的例子。

    #include <iostream>
    #include <string>
    using namespace std;
    void func(string str){
        cout<<str<<endl;
    }
    int main(){
        string s1 = "http://c.ttt.net";
        string s2(s1);
        string s3 = s1;
        string s4 = s1 + " " + s2;
        func(s1);
        cout<<s1<<endl<<s2<<endl<<s3<<endl<<s4<<endl;
       
        return 0;
    }
    运行结果:
    http://c.ttt.net
    http://c.ttt.net
    http://c.ttt.net
    http://c.ttt.net
    http://c.ttt.net http://c.ttt.net
    

    s1、s2、s3、s4 以及 func() 的形参 str,都是使用拷贝的方式来初始化的。
    对于 s1,表面上看起来是将一个字符串直接赋值给了 s1,实际上在内部进行了类型转换,将 const char * 类型转换为 string 类型后才赋值的。s4 也是类似的道理。

    对于 s1、s2、s3、s4,都是将其它对象的数据拷贝给当前对象,以完成当前对象的初始化。

    对于 func() 的形参 str,其实在定义时就为它分配了内存,但是此时并没有初始化,只有等到调用 func() 时,才会将其它对象的数据拷贝给 str 以完成初始化。

    当以拷贝的方式初始化一个对象时,会调用一个特殊的构造函数,就是拷贝构造函数(Copy Constructor)。

    下面的例子演示了拷贝构造函数的定义和使用:

    #include <iostream>
    #include <string>
    using namespace std;
    class Student{
    public:
        Student(string name = "", int age = 0, float score = 0.0f);  //普通构造函数
        Student(const Student &stu);  //拷贝构造函数(声明)
    public:
        void display();
    private:
        string m_name;
        int m_age;
        float m_score;
    };
    Student::Student(string name, int age, float score): m_name(name), m_age(age), m_score(score){ }
    //拷贝构造函数(定义)
    Student::Student(const Student &stu){
        this->m_name = stu.m_name;
        this->m_age = stu.m_age;
        this->m_score = stu.m_score;
       
        cout<<"Copy constructor was called."<<endl;
    }
    void Student::display(){
        cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
    }
    int main(){
        Student stu1("小明", 16, 90.5);
        Student stu2 = stu1;  //调用拷贝构造函数
        Student stu3(stu1);  //调用拷贝构造函数
        stu1.display();
        stu2.display();
        stu3.display();
       
        return 0;
    }
    

    运行结果:

    Copy constructor was called.
    Copy constructor was called.
    小明的年龄是16,成绩是90.5
    小明的年龄是16,成绩是90.5
    小明的年龄是16,成绩是90.5
    

    第 8 行是拷贝构造函数的声明,第 20 行是拷贝构造函数的定义。拷贝构造函数只有一个参数,它的类型是当前类的引用,而且一般都是 const 引用。

    1) 为什么必须是当前类的引用呢?

    如果拷贝构造函数的参数不是当前类的引用,而是当前类的对象,那么在调用拷贝构造函数时,会将另外一个对象直接传递给形参,这本身就是一次拷贝,会再次调用拷贝构造函数,然后又将一个对象直接传递给了形参,将继续调用拷贝构造函数……这个过程会一直持续下去,没有尽头,陷入死循环。

    只有当参数是当前类的引用时,才不会导致再次调用拷贝构造函数,这不仅是逻辑上的要求,也是 C++ 语法的要求。

    2) 为什么是 const 引用呢?

    拷贝构造函数的目的是用其它对象的数据来初始化当前对象,并没有期望更改其它对象的数据,添加 const 限制后,这个含义更加明确了。

    另外一个原因是,添加 const 限制后,可以将 const 对象和非 const 对象传递给形参了,因为非 const 类型可以转换为 const 类型。如果没有 const 限制,就不能将 const 对象传递给形参,因为 const 类型不能转换为非 const 类型,这就意味着,不能使用 const 对象来初始化当前对象了。

    以上面的 Student 类为例,将 const 去掉后,拷贝构造函数的原型变为:
    Student::Student(Student &stu);

    此时,下面的代码就会发生错误:

    const Student stu1("小明", 16, 90.5);
    Student stu2 = stu1;
    Student stu3(stu1);
    

    stu1 是 const 类型,在初始化 stu2、stu3 时,编译器希望调用Student::Student(const Student &stu),但是这个函数却不存在,又不能将 const Student 类型转换为 Student 类型去调用Student::Student(Student &stu),所以最终调用失败了。

    当然,你也可以再添加一个参数为 const 引用的拷贝构造函数,这样就不会出错了。换句话说,一个类可以同时存在两个拷贝构造函数,一个函数的参数为 const 引用,另一个函数的参数为非 const 引用。

    默认拷贝构造函数

    在前面的教程中,我们还没有讲解拷贝构造函数,但是却已经在使用拷贝的方式创建对象了,并且也没有引发什么错误。这是因为,如果程序员没有显式地定义拷贝构造函数,那么编译器会自动生成一个默认的拷贝构造函数。这个默认的拷贝构造函数很简单,就是使用“老对象”的成员变量对“新对象”的成员变量进行一一赋值,和上面 Student 类的拷贝构造函数非常类似。

    对于简单的类,默认拷贝构造函数一般是够用的,我们也没有必要再显式地定义一个功能类似的拷贝构造函数。但是当类持有其它资源时,如动态分配的内存、打开的文件、指向其他数据的指针、网络连接等,默认拷贝构造函数就不能拷贝这些资源,我们必须显式地定义拷贝构造函数,以完整地拷贝对象的所有数据。

    更多相关内容
  • 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 1.复制构造函数和重载赋值操作符的定义; 2.复制构造函数和重载赋值操作符的调用时机; 3.复制构造函数和重载赋值操作符的实现要点; 4....
  • 在C++中,只有单个形参,而且该类型是对本类类型的引用(常用const修饰),这样的构造函数称为复制构造函数。  复制构造函数既可以自己定义又可以像默认构造函数一样被编译器隐式调用。但大多数时候,特别是类中...
  • C++中的复制构造函数

    2022-01-30 23:29:20
    (什么是复制构造函数?) A copy constructor is a member function that initializes an object using another object of the same class. A copy constructor has the following general function prototype: ...

    Copy Constructor in C++

    What is a copy constructor? (什么是复制构造函数?)

    A copy constructor is a member function that initializes an object using another object of the same class. A copy constructor has the following general function prototype:

    复制构造函数是一个成员函数,它使用同一类的另一个对象来初始化一个对象。复制构造函数具有以下通用函数原型:

    ClassName (const ClassName &old_obj); 
    

    Following is a simple example of copy constructor.

    #include<iostream>
    using namespace std;
    
    class Point
    {
    private:
        int x, y;
    public:
        Point(int x1, int y1) { x = x1; y = y1; }
    
        // Copy constructor
        Point(const Point &p1) {x = p1.x; y = p1.y; }
    
        int getX()       { return x; }
        int getY()       { return y; }
    };
    
    int main()
    {
        Point p1(10, 15); // Normal constructor is called here
        Point p2 = p1; // Copy constructor is called here
    
        // Let us access values assigned by constructors
        cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();
        cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();
    
        return 0;
    }
    
    

    Output:

    p1.x = 10, p1.y = 15
    p2.x = 10, p2.y = 15 
    

    When is copy constructor called? (什么时候复制构造函数被调用?)

    In C++, a Copy Constructor may be called in the following cases:

    1. When an object of the class is returned by value.
    2. When an object of the class is passed (to a function) by value as an argument.
    3. When an object is constructed based on another object of the same class.
    4. When the compiler generates a temporary object.

    It is, however, not guaranteed that a copy constructor will be called in all these cases, because the C++ Standard allows the compiler to optimize the copy away in certain cases, one example is the return value optimization (sometimes referred to as RVO).

    在 C++ 中,在以下情况下可能会调用 Copy Constructor:

    1. 当类的对象按值return时。
    2. 当类的对象按值作为参数传递(给函数)时。
    3. 当一个对象是基于同一个类的另一个对象构造时。
    4. 当编译器生成临时对象时。

    但是,不能保证在所有这些情况下都会调用复制构造函数,因为 C++ 标准允许编译器在某些情况下优化复制,例如返回值优化(有时称为 RVO)

    When is a user-defined copy constructor needed? (什么时候需要用户自定义复制构造函数)

    If we don’t define our own copy constructor, the C++ compiler creates a default copy constructor for each class which does a member-wise copy between objects. The compiler created copy constructor works fine in general. We need to define our own copy constructor only if an object has pointers or any runtime allocation of the resource like filehandle, a network connection…etc.

    The default constructor does only shallow copy.

    如果我们不定义自己的复制构造函数,C++ 编译器会为每个类创建一个默认的复制构造函数,它会在对象之间进行成员方式的复制。编译器创建的复制构造函数通常可以正常工作。仅当对象具有指针或资源的任何运行时分配(如文件句柄、网络连接等)时,我们才需要定义自己的复制构造函数。

    默认构造函数只做浅拷贝

    shallow copy

    Deep copy is possible only with user defined copy constructor. In user defined copy constructor, we make sure that pointers (or references) of copied object point to new memory locations.

    只有使用用户定义的复制构造函数才能进行深度复制。在用户定义的复制构造函数中,我们确保复制对象的指针(或引用)指向新的内存位置

    deep copy

    Copy constructor vs Assignment Operator(复制构造函数 vs 赋值运算符 )

    以下两个语句中,哪一个调用了复制构造函数,哪一个调用了赋值运算符?

    MyClass t1, t2;
    MyClass t3 = t1; // ----> (1)
    t2 = t1;         // -----> (2)
    

    Copy constructor is called when a new object is created from an existing object, as a copy of the existing object. Assignment operator is called when an already initialized object is assigned a new value from another existing object. In the above example (1) calls copy constructor and (2) calls assignment operator.

    当从现有对象创建新对象时调用复制构造函数,作为现有对象的副本。当已经初始化的对象从另一个现有对象分配新值时,将调用赋值运算符。在上面的例子中 (1) 调用复制构造函数, (2) 调用赋值运算符。

    Write an example class where copy constructor is needed? (需要编写复制构造函数的示例类)

    Following is a complete C++ program to demonstrate use of Copy constructor. In the following String class, we must write copy constructor.

    下面是一个完整的 C++ 程序来演示 Copy 构造函数的使用。在下面的 String 类中,我们必须编写复制构造函数。

    #include<iostream>
    #include<cstring>
    using namespace std;
    
    class String
    {
    private:
        char *s;
        int size;
    public:
        String(const char *str = NULL); // constructor
        ~String() { delete [] s; }// destructor
        String(const String&); // copy constructor
        void print() { cout << s << endl; } // Function to print string
        void change(const char *); // Function to change
    };
    
    String::String(const char *str)
    {
        size = strlen(str);
        s = new char[size+1];
        strcpy(s, str);
    }
    
    void String::change(const char *str)
    {
        delete [] s;
        size = strlen(str);
        s = new char[size+1];
        strcpy(s, str);
    }
    
    String::String(const String& old_str)
    {
        size = old_str.size;
        s = new char[size+1];
        strcpy(s, old_str.s);
    }
    
    int main()
    {
        String str1("GeeksQuiz");
        String str2 = str1;
    
        str1.print(); // what is printed ?
        str2.print();
    
        str2.change("GeeksforGeeks");
    
        str1.print(); // what is printed now ?
        str2.print();
        return 0;
    }
    
    

    Output:

    GeeksQuiz
    GeeksQuiz
    GeeksQuiz
    GeeksforGeeks
    

    What would be the problem if we remove copy constructor from above code? (如果我们从上面的代码中删除复制构造函数会出现什么问题? )

    If we remove copy constructor from the above program, we don’t get the expected output. The changes made to str2 reflect in str1 as well which is never expected.

    如果我们从上述程序中删除复制构造函数,我们不会得到预期的输出。对 str2 所做的更改也反映在 str1 中,这是意料之外的。

    #include<iostream>
    #include<cstring>
    using namespace std;
    
    class String
    {
    private:
        char *s;
        int size;
    public:
        String(const char *str = NULL); // constructor
        ~String() { delete [] s; }// destructor
        void print() { cout << s << endl; }
        void change(const char *); // Function to change
    };
    
    String::String(const char *str)
    {
        size = strlen(str);
        s = new char[size+1];
        strcpy(s, str);
    }
    
    void String::change(const char *str)
    {
        delete [] s;
        size = strlen(str);
        s = new char[size+1];
        strcpy(s, str);
    }
    
    int main()
    {
        String str1("GeeksQuiz");
        String str2 = str1;
    
        str1.print(); // what is printed ?
        str2.print();
    
        str2.change("GeeksforGeeks");
    
        str1.print(); // what is printed now ?
        str2.print();
        return 0;
    }
    
    

    Output:

    GeeksQuiz
    GeeksQuiz
    GeeksforGeeks
    GeeksforGeeks
    

    Can we make copy constructor private? (我们可以将复制构造函数设为私有吗? )

    Yes, a copy constructor can be made private. When we make a copy constructor private in a class, objects of that class become non-copyable. This is particularly useful when our class has pointers or dynamically allocated resources. In such situations, we can either write our own copy constructor like above String example or make a private copy constructor so that users get compiler errors rather than surprises at runtime.

    是的,可以将复制构造函数设为私有。当我们在类中将复制构造函数设为私有时,该类的对象将变为不可复制。当我们的类有指针或动态分配的资源时,这特别有用。在这种情况下,我们可以像上面的 String 示例那样编写自己的复制构造函数,也可以创建一个私有的复制构造函数,以便用户在运行时得到编译器错误而不是意外。

    Why argument to a copy constructor must be passed as a reference? (为什么必须将复制构造函数的参数作为引用传递? )

    A copy constructor is called when an object is passed by value. Copy constructor itself is a function. So if we pass an argument by value in a copy constructor, a call to copy constructor would be made to call copy constructor which becomes a non-terminating chain of calls. Therefore compiler doesn’t allow parameters to be passed by value.

    当对象按值传递时,将调用复制构造函数。复制构造函数本身就是一个函数。因此,如果我们在复制构造函数中按值传递参数,则对复制构造函数的调用将成为调用复制构造函数的非终止链。因此编译器不允许参数按值传递。

    Why argument to a copy constructor should be const? (为什么复制构造函数的参数应该是 const? )

    See https://www.geeksforgeeks.org/copy-constructor-argument-const/

    展开全文
  • 在函数中自动调用复制构造函数 #include #include #include using namespace std; class A { public: A(string name):_name(name) { cout << 类名为: << _name << ,调用构造函数,构造对象地址...
  • 我们知道,类、struct结构体的复制构造函数在三种情况下会被调用,分别是: 1、创建对象a时,使用对象b去初始化对象a 2、函数fun( )的形参是对象时,传递参数时,复制构造函数会被调用 3、函数的返回值是对象时,...

    前言

    如果真的想明白,为什么你写的函数的返回值是对象时,有的时候调用了复制构造函数,而有的时候,没有调用复制构造函数。需要明白一件事:函数的返回值是对象时,什么情况下,函数的返回是return by value,即生成了临时对象。这两个问题是等价的,因为按照道理来讲,对象作为函数的返回值时,就应该生成临时对象,调用复制构造函数,初始化这个临时对象,但是,现在的编译器,对于return by value有优化。有时候,不生产临时对象。编译器总是将栈里面的 结果对象 直接给调用者,避免了多一次的析构和构造。即使在关闭编译器优化的时候,它依然给你做了这个动作。
    现在的我,还不明白具体什么情况下,生成临时对象。什么情况下,不生成临时对象。
    留给以后解决
    可以参考的文章参考1参考2

    三种用法

    我们知道,类、struct结构体的 复制构造函数 在三种情况下会被调用,分别是:

    1、创建对象a时,使用对象b去初始化对象a
    2、函数fun( )的形参是对象时,传递参数时,形参 是用 复制构造函数 初始化的
    3、函数的返回值是对象时,生成的临时对象,是用 复制构造函数初始化的

    接下来依次看一下这三种情况。

    首先是类的定义

    class node {
        public:
            int x, y;
            
            node () {} // 默认构造函数
            node (int _x, int _y) : x(_x), y(_y) {} // 自定义的构造函数
    
            node (const node& temp) { // 自定义的复制构造函数
                x = temp.x;
                y = temp.y;
                printf("复制构造函数被调用\n"); 
                // 打印语句,只要该复制构造函数被调用,就会有输出
            }
    };
    

    1、创建对象a时,使用别的对象b去初始化a

    int main()
    {
        node b(8, 10); 
        // 创建了node对象 b,并且调用了构造函数,初始化对象b
        node a(b); // 创建对象a, 并且使用对象b来初始化a
        // node a = b; 
        // 这样也可以,只要是创建对象同时,用其余对象初始化就可以。
        cout<< a.x << " " << a.y << endl;
    
        return 0;
    }
    
    

    运行结果

    复制构造函数被调用
    8 10
    

    可以看到,先输出了"复制构造函数被调用",之后又执行了打印a.x、a.y的语句。说明,在创建对象a时,使用对象b初始化对象a,是会调用复制构造函数的。调用了对象a的复制构造函数,复制构造函数的实参是对象 b。
    但是下面的这种情况就不是创建对象的同时使用另一个对象初始化了。

    int main()
    {
        node b(8, 10);
        node a; // 先创建了对象 a,使用默认构造函数初始化了对象 a
    
        a = b; // 这是赋值,不是初始化
        
        cout << a.x << " " << a.y << endl;
    
        return 0;
    }
    

    运行结果

    8 10
    

    可见,首先定义一个对象a,之后再进行赋值,就不是初始化了。

    2、函数fun( )的形参是对象时,传递参数时,形参 是用 复制构造函数 初始化的

    void show(node temp) { // show()函数的参数是对象
        cout << temp.x << " " << temp.y << endl;
    }
    
    int main()
    {
        node a(8, 10); 
        // 创建了node对象 a,并且调用了构造函数,初始化数据
    
        show(a);
        // 将对象a作为函数的实参传入
    
        return 0;
    }
    

    运行结果

    复制构造函数被调用
    8 10
    

    可以看到,首先打印的是复制构造函数中的语句“复制构造函数被调用”,之后执行的是函数体里面的打印语句。所以,对象作为函数的参数时,传递参数时,是会调用复制构造函数的。即进行了值复制。形参temp是使用实参 a 初始化的。初始化形参temp时,调用了temp的复制构造函数,这个函数的参数是a。
    更进一步,如果参数是引用:

    void show(node& temp) { // show()函数的参数是 对象的引用
        cout << temp.x << " " << temp.y << endl;
    }
    
    int main()
    {
        node a(8, 10); 
        // 创建了node对象 a,并且调用了构造函数,初始化数据
    
        show(a);
        // 将对象a作为函数的实参传入
    
        return 0;
    }
    

    此时函数的参数不是对象本身,而是对象的引用。再来看一下运行结果:

    8 10
    

    如果函数的参数是 对象的引用时,传入参数的时候,没有调用复制构造函数,没有输出复制构造函数中的打印语句“复制构造函数被调用”。
    所以这种情况下,就没有复制这一步,因为传入的是实参的引用。如果大量数据要作为实参传入时,最好传入的是引用。速度快。

    3、 函数的返回值是对象时,生成的临时对象,是用 复制构造函数 初始化的

    我们知道,不同的函数,是在不同的内存空间中运行的,调用某个函数fun()时,去往另一片内存空间运行,当该函数执行结束时,这一片内存空间要被释放,其中的变量要消亡。但是,如果该函数有返回值(无论是基本类型,还是对象),则该函数要生成一个临时的值temp,用于返回给调用函数的地方。因为该函数执行结束之后,该函数里面的任何局部变量都要消失。
    重点就是,该临时值temp,如果是对象的话,就是使用复制构造函数进行初始化的。

    node create_node() { // 函数的返回值是对象
        node temp(3, 5); // 创建一个对象
        return temp; // 返回对象
    }
    
    int main()
    {
        node a = create_node();// a 用于接收函数返回值
    
        cout << a.x << " " << a.y << endl;
    
        return 0;
    }
    

    运行结果:

    3 5
    

    可以看到,并没有想象的那样输出“复制构造函数被调用”,也就是说明,此种情况下,并没有调用复制构造函数。
    原因在于现在的编译器对于“对象作为函数的返回值”这种情况有所优化。返回值为对象时,有的情况下不再产生临时对象,因而不再调用复制构造函数。因此也就不存在临时对象被初始化。

    有几种情况下,还是会调用复制构造函数。
    全局对象 cur 作为函数返回值,对象 a 在接收 返回值 cur 时,调用复制构造函数。

    #include <bits/stdc++.h>
    using namespace std;
    
    class node {
        public:
            int x, y;
    
            node() {}
            node(int _x, int _y) : x(_x), y(_y) {}
    
            node(const node& temp) {
                x = temp.x, y = temp.y;
                printf("复制构造函数被调用\n");
            }
    };
    
    node cur(1, 2);
    
    node fun() { // 函数的返回值是对象,并且是全局对象
        return cur;
    }
    
    int main()
    {
        node a ; // a用于接收函数返回值
        a = fun(); // a 接收函数返回值
        cout << a.x << " " << a.y << endl;
    
        return 0;
    }
    

    运行结果

    复制构造函数被调用
    1 2
    

    可以看到,当全局对象 cur 作为函数返回值时,对象 a 在接收 返回对象 cur时,调用了复制构造函数。

    详细地分析一下这个例子。fun( ) 函数执行结束后,会生成一个临时对象,假设为temp,该对象是使用对象cur进行初始化的。在这个初始化过程中,调用了temp的复制构造函数,其实参是 return 语句中的 cur。在这之后,fun( )函数彻底消失,占用的内存被释放。
    之后,在main( ) 函数中的 a = fun(); 语句,该临时对象temp,被赋值给 a, 这是一个赋值语句,并不是初始化语句。该语句执行结束之后,临时对象 temp 消亡。

    此外,如果有一个函数,它的参数是对象,返回值也是对象,则,接收该函数的返回值时,调用了复制构造函数。

    #include <bits/stdc++.h>
    using namespace std;
    
    class node {
        public:
            int x, y;
    
            node() {}
            node(int _x, int _y) : x(_x), y(_y) {}
    
            node(const node& temp) {
                x = temp.x, y = temp.y;
                printf("复制构造函数被调用\n");
            }
    };
    
    node fun(node& temp) { // 此处是引用,否则调用两次赋值构造函数。
        return temp;
    }
    
    int main()
    {
        node a(1, 2), b;
        b = fun(a);
        cout << a.x << " " << a.y << endl;
    
        return 0;
    }
    

    运行结果

    复制构造函数被调用
    1 2
    

    可以看到,fun( ) 函数的参数是对象、返回值也是对象,此时,对象 b 接收 返回值时,就调用了复制构造函数。

    总结

    复制构造函数,只在对象被 初始化 时被调用。而初始化意味着某对象在创建的同时,被初始化。
    具体就三种情况:
    1、对象被创建时,被其余对象初始化
    2、函数fun( ) 的形参是对象,则调用函数 fun()时,形参会被初始化
    3、函数的返回值是 对象,则生成的临时对象会被 return 语句中的数据初始化。

    展开全文
  • C++类用三个特殊的成员函数:复制构造函数、赋值操作符和析构函数 来决定类对象之间的初始化或赋值时发生什么。所谓的“复制控制”即通过这三个成员函数控制对象复制的过程。本篇文章将介绍复制构造函数。  复制...
  • 构造函数 是为了让我们自己去规定 , 我们定义对象的初始化。 #include <iostream> using std::cin; using std::cout; using std::endl; class Clock{ public: void setTime(int newS = 0, int newM = 0, ...

    一、构造函数

    是为了让我们自己去规定 , 我们定义对象的初始化。
    这里给出一段程序,作用是按格式输出 输入的时间。

    #include <iostream>
    using std::cin;
    using std::cout;
    using std::endl;
    class Clock{ 
        public:
        	void setTime(int newS = 0, int newM = 0, int newH = 0){
        	//这一步如果想要输出默认值,应该把主函数里的setTime函数中的参数删除。
                second = newS;
                minute = newM;
                hour = newH; 
            }
        	void showTime();
        private:
            int second, minute, hour;
    };
    void Clock::showTime(){
       cout << hour << ":" << minute << ":" << second <<endl;
    }
    int main(){
        Clock MyClock;
        int second, minute, hour;
        cin >> second >> minute >> hour;
        MyClock.setTime(second, minute, hour);
        MyClock.showTime();
        return 0;
    }
    

    构造函数的性质:
    ● 不能定义返回值类型,也不能写return语句
    ●可以有形参,也可以没有形参,可以带有默认参数
    ●函数名必须和类名完全相同
    ●可以重载

    当我们没有写构造函数时,系统会为我们自动生成一个默认构造函数,这个默认构造函数的参数列表和内容都是空的,所以他什么都不会做!

    现在 我们需要创建一个默认构造函数,让对象早初始化的时候把所有的时间全部设置为0。

    //在类内定义构造函数时,不能有返回值类型,且构造函数的名字要和类一样,可以有形参也可以没有形参,这里的第一种定义是没有形参的类型:
    class Clock{
    public:
        Clock(){
            second = 0;
            minute = 0;
            hour = 0;
        }
    //如果不写构造函数,那么系统自动生成的构造函数 就是
        Clock(){}
    //函数里没有任何内容,所以也不会有操作
    

    第二种定义类型是有参数的形式:

    Class Clock{
    public:
    	Clock(int newS, int newM, int newH){
        second = newS;
        minute = newM;
        hour = newH;
    }
    }
    
    //或者在类外实现构造函数
    Class Clock{
    public:
    	Clock(int newS, int newM, int newH);
    	}
    Clock::Clock(int newS, int newM, int newH){
        second = newS;
        minute = newM;
        hour = newH;
    }
    这里在类外实现构造函数时,函数名前面仍然不要写函数的返回值类型 ,
    实现类外构造函数 : 在类内对函数定义,然后在类外 用 
     类名 :: 函数名 ( 参数 ) { 函数体 }   的形式在类外实现构造函数。 
    
    而在类外实现普通函数时,也是在类内先对函数进行定义,然后在类外 用
    返回值类型 类名 :: 函数名 ( 参数 ) { 函数体 } 实现,这里需要在类
    名前面加上返回值类型
    

    调用带参数的构造函数时,是在对象创建时被调用的:

     Clock MyClock2(1,2,3);
    

    二、复制构造函数

    用一个已有的对象,来执行一个新的对象的构造
    ●复制构造函数具有一般构造函数的所有特性,它的形参是本类的一个对象的引用,作用是用一个已经存在的对象(函数的参数)来初始化一个新的对象。
    ●复制构造函数的的使用方式 就是引用传参

    引用就是 给一个已经存在的变量起一个别名,所以用它的变量名或者别名都可以操作该变量, 复制构造函数就是给已经存在的构造函数起一个别名。

    因为:值传递就是当函数发生调用的时候,给形参分配内存空间,然后用实参的值来初始化形参,但是如果参数是一个对象,值传递“初始化形参”这个过程就会造成很多额外的时间开销,浪费系统资源,如果使用引用就不会有这样的问题。

    复制构造函数能实现同类对象之间数据成员的传递。 当我们没有定义类的构造韩函数时,系统就会在必要的时候自动生成一个隐含的复制构造函数,他的作用是把初始对象的每一个数据成员的值都复制到新建的对象中,这就完成了同类对象的复制。
    复制构造函数的具体声明方式 :

    class 类名{
    public:
    	类名(类名& 对象名){
    	//实现
    	}
    };
    

    实例:
    先有一个point类,表示屏幕上的一个点——它包括两个 int 类的私有成员 x , y, 表示这个点的坐标。

    class Point {
    public:
    	Point(Point &p);
    private:
    	int x,y;
    };
    
    //以下代码可实现复制构造函数
    Point::Point(Point &p){
    	x = p.x;
    	y = p.y;
    }
    //这里复制构造函数访问了Point类的实例对象p的两个私有成员变量 x,y 。我们需要注意的是 ———— private 与 public 的区别是对类来说的,而不是对对象来说。 拷贝构造函数是Point类的成员函数————所以它可以访问类的私有成员变量,这跟具体的对象无关。
    

    复制构造函数被调用主要的三种情况:
    ●当用类的一个对象去初始化该类的另一个对象的时候:

    Point a(1,2);
    Point b(a);//用对象a初始化对象b,复制构造函数被调用
    Point c = b;//用对象b给对象c赋值,复制构造函数被调用
    

    ●当函数的形参是类的对象,调用函数是进行形式结合的时候:

    void f(Point p){
        //函数体
    }
    int main(){
        Point a(1,2);
        f(a);
        return 0;
    }
    

    ●当函数返回值是类的对象,函数执行完成返回调用者的时候:

    Point g(){
        Point a(1,2);
        return a;
    }
    /*定义在函数中的变量都是局部变量,当函数返回值的时候这些局部变量
    都被销毁了。同样,在函数中创建的对象也相同。 return a 这一步返
    回的并不是 a 这个对象本身,而是通过复制构造函数,在主调函数中用a
    重新构造的对象。在函数调用返回的时候,原来的临时对象 a 的使命已
    经完成,随着整个函数中的其他临时变量一起被销毁了。*/
    

    对于复制构造函数,我们还可以自己实现一些有选择、有变化的复制:

    Point (point &p){
        x = p.x+10;
        p=p.y;
    }
    

    这个函数就会把每一个由复制构造得到的Point对象,横坐标+10.

    展开全文
  • 复制构造函数

    2021-02-14 03:20:41
    复制构造函数
  • 关于复制构造函数的简单介绍,可以看我以前写过的一篇文章C++复制控制之复制构造函数该文章中介绍了复制构造函数的定义、调用时机、也对编译器合成的复制构造函数行为做了简单说明。本文因需要会涉及到上文的一些...
  • C++——构造函数、析构函数以及复制构造函数

    千次阅读 多人点赞 2021-03-09 16:28:49
    文章目录一、构造函数1.1 构造函数是什么?1.2 为什么要有构造函数?1.3 如何使用构造函数?1.4 构造函数的实现二、使用步骤 一、构造函数 在程序执行的过程中,当遇到与对声明语句时,程序会向操作系统申请一定的...
  • 假设基类使用了动态内存分配,而且定义了析构函数、复制构造函数和赋值函数,但是在派生类中没有使用动态内存分配,那么在派生类中不需要显示定义析构函数、复制构造函数和赋值函数。 当基类和派生类采用动态内存...
  • 如果类成员都是简单类型(如标量值),则编译器生成的复制构造函数已足够,无需定义自己的类型。 如果类需要更复杂的初始化,则需要实现自定义复制构造函数。例如,如果类成员是指针,则需要定义复制构造函数以分配...
  • c++复制构造函数详解

    千次阅读 多人点赞 2020-07-12 16:32:41
    复制构造函数(Copy constructor)是c++中的一个特殊构造函数,也称拷贝构造函数,它只有一个参数,参数类型为同类对象的引用。 如果没有定义复制构造函数,那么编译器将生成默认的复制构造函数。默认的复制构造函数...
  • c++ 复制构造函数 用C ++复制构造函数 (Copy Constructor in C++) Copy Constructor is a type of constructor which is used to create a copy of an already existing object of a class type. It is usually of ...
  • 文章目录默认构造函数和复制构造函数1 定义默认构造函数2 隐式类类型转换3 复制构造函数与合成复制构造函数3.1 复制构造函数3.2 合成复制构造函数4 深复制与浅复制 默认构造函数和复制构造函数 1 定义默认构造函数 ...
  • 复制构造函数是一种特殊的构造函数,有一般构造函数的特性。它的功能是用一个已知的对象来初始化一个被创建的同类对象。复制构造函数的参数传递方式必须按引用来进行传递,请看实例: #include #include using ...
  • 一、复制构造函数定义 复制构造函数是一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已知的对象去初始化同类型的新对象。 class 类名 { public: 类型(形参);//构造函数 类名(const 类名 &...
  • 拷贝和复制是一个意思,对应的英文单词都是copy。对于计算机来说,拷贝是指用一份原有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的...
  • 通常,复制构造函数是通过使用先前已创建的相同类的对象初始化对象来创建对象的构造函数。Java支持复制构造函数,但与C语言不同,Java没有提供您需要自己定义的显式复制构造函数。写一个拷贝构造函数通常,为了初始...
  • 上篇说到C++的构造函数与析构函数的使用 本次学习下构造函数的进阶版 ...//复制构造函数 product& operator=(const product& other);//复制赋值构造函数 private: QString m_name; int m_money;
  • 拷贝构造函数(复制构造函数) 一、拷贝构造函数概念 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象...
  • 在C++中复制控制是一个比较重要的话题,主要包括复制构造函数、重载赋值操作符、析构函数这三部分,这三个函数是一致的,如果需要手动定义了其中了一个,那么另外的两个也需要定义,通常在存在指针或者前期相关操作...
  • 拷贝构造函数(复制构造函数

    千次阅读 2019-03-17 15:44:34
    赋值构造函数 用一个对象初始化另一个对象 特别注意:初始化操作和 等号操作是两个不同的概念 1、初始化法 Class A { public: A(int a) { m_a = a; } A(int a,int b) { m_a = a; m_b = b; } A(const A...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 343,263
精华内容 137,305
关键字:

复制构造函数

友情链接: 8970268.rar