精华内容
下载资源
问答
  • 赋值构造函数

    2019-09-08 14:59:01
    什么是赋值构造函数 *当程序执行f2=f1时,自动调用赋值构造函数,也是浅拷贝(位拷贝).* 如下代码: Human f1, f2; f2 = f1; f1.description(); f2.description(); cout << "------------------" &...

    一.什么是赋值构造函数

    当程序执行f2=f1时,自动调用赋值构造函数,也是浅拷贝(位拷贝).
    如下代码:

    Human f1, f2;
    	f2 = f1;		
    	f1.description();
    	f2.description();
    	cout << "------------------" << endl;
    	f1.setAddr("新加坡");
    	f1.description();
    	f2.description();
    

    图1
    如图所示,当f1赋值给f2后,f1数据改变,f2也跟着改变,这是很不安全的.

    二.如何解决浅拷贝带来的问题

    *进行深拷贝,即"="运算符重载函数,用strcpy_s(addr,ADDR_LEN,other.addr);来进行深拷贝*
    代码如下:
    
    Human &operator=(const Human &other);
    Human &Human::operator=(const Human &other) {
    	//f1=f1;自己赋给自己,无意义,this是该对象的指针,&取地址赋给this指针
    	if (this==&other) {
    		return *this;
    	}
    	//比如执行:f1=f2
    	//就会调用:f1.operator=(f2);
    	name = other.name;
    	age = other.age;
    	salary = other.salary;
    
    	//深拷贝
    	strcpy_s(addr,ADDR_LEN,other.addr);
    	return *this;
    }
    
    ## 三.什么时候用赋值构造函数
    *显而易见,但对象之间进行赋值操作时,需要用到赋值构造函数.*
    
    
    展开全文
  • 以上就是小编为大家带来的C++ 赋值构造函数注意点介绍全部内容了,希望大家多多支持软件开发网~ 您可能感兴趣的文章:浅谈c++构造函数问题,初始化和赋值问题详解C++ 拷贝构造函数和赋值运算符详解C++中对构造函数和...
  • C++ 拷贝构造函数 赋值构造函数 解释
  • 类的赋值构造函数和复制构造函数

    千次阅读 2016-10-31 22:41:49
    闲来无事,整理一下,一个对象的赋值构造函数赋值构造函数。整体的说一下,复制构造函数和赋值构造函数的相同点是: 赋值运算符和复制构造函数都是用已存在的B对象来创建另一个对象A; 最大的不同在于:赋值构造...

    C++的初学者经常会对复制构造函数一知半解,我曾经对复制构造函数和赋值函数就很是迷茫。闲来无事,整理一下,一个对象的复制构造函数和赋值构造函数。整体的说一下,复制构造函数和赋值构造函数的相同点是: 赋值运算符和复制构造函数都是用已存在的B对象来创建另一个对象A;
    最大的不同在于:赋值构造函数处理两个已有对象,即赋值前B应该是存在的;复制构造函数是生成一个全新的对象,即调用复制构造函数之前A不存在。

    - 类

    首先,介绍一下类:类是数据以及用于操纵该数据的方法(函数)的集合,是逻辑上相关函数与数据的封装。它是对所要处理问题的抽象描述,它把数据(事物的属性)和函数(事物的行为/操纵)封装为一个整体。
    类的定义格式如下:

    class 类名{
    private//私有数据和函数
    protect:
    //保护数据和函数
    public//公有数据和函数
    }

    C++中面向对象的思想是对对象的属性(成员变量)进行操作,应该通过对象的方法(成员函数)来进行,对象的方法是对象和外部的接口。
    这里顺便说一句成员函数的定义通常的格式是:

    范围值类型 类名::函数名(参数表)
    {
    //函数体
    }

    很多基础比较差的程序员,“::”符号不知道是什么意思(虽然知道也没什么用),只知道怎么用!运算符“::”成为作用域解析运算符,它指出该函数是属于哪一类的成员函数。当然也可以在类的定义中直接定义函数。所以有的时候也不用“::”符号.

    对象即类的实例。创建类的对象可以常用的两种方法:
    1、直接创建对象,比如CObject类的一个对象 CObject object;(注意这里的object是一个临时对象)
    2、采用动态创建类的对象的方法,当然遍历同样也可以动态创建。

    - 复制构造函数

    上面说到了类,也说到了对象的创建,那么同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制是完全可行的。这个复制的过程只需要把数据成员复制过来即可,而函数成员是可以共用的,同一个类的任何对象都可以共用这个类的函数成员。
    在建立对象时可以用同一类的另一个对象来初始化该对象,这时所用的构造函数成为复制构造函数。像X::X(X&),这种形式,且只有一个参数——同类的对象,采用的是引用的方式。不能用X::X(X)这种形式的构造函数,如果把一个真实的类的对象作为参数传递到复制构造函数,会引起无穷的递归。那很多人有不解,平常我创建类的对象的时候也没有写复制构造函数呀,为什么也没有报错呢?原因是如果没有定义的话,那么编译器生成默认复制构造函数;如果定义了地自己的复制构造函数,则默认的复制构造函数就不存在了。(这一点与析构函数不同,即使自己定义了析构函数,默认的析构函数仍然存在)
    复制构造函数在以下3中情况下会被调用:

    1. 当用一个对象去初始化同类的另一个对象时。
    CObject o2(o1);
    //
    CObject o2=o1;
    1. 如果某函数有一个形参是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用。
    void Fun(CObject object)
    {
        object.a=1;
    }
    CObject object;
    Fun(object);
    //CObject 的复制构造函数被调用,生成形参,在内存新建一个局部对象,并把实参复制到新的对象中。
    1. 如果函数的返回值是类CObject的对象,则函数返回时,CObject的复制构造函数被调用。理由也是建立一个临时对象,再返回调用者。
    CObject Fun()
    {
        CObject object;
        return object; //在此处调用了CObject(object)
    }
    int main()
    {
        CObject object; //此处是一个局部对象,也是一个临时对象
        object = Fun(); 
        return 0;
    }

    有人可能会问,为什么不直接用要返回的局部对象呢?因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存。所以编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓返回对象,实际上就是调用复制构造函数把该对象的值复制到临时对象中。

    -赋值构造函数

    首先看一段代码:

    int main()
    {
        CObject theObjectOne;
        theObjectOne.Init(100);
    
        CObject theObjectTwo;
        theObjectTwo.Init(200);
    
        theObjectTwo = theObjectOne;
        //此操作是对象赋值操作,更深一步的操作时将被复制对象theObjectTwo对象原有的内容清除,并用theObjectOne对象里的内容填充。
        return 0;
    }

    “=”就是讲一个对象里的内容到另一个对象中去,这其中涉及到对象原有内容的丢弃和新内容的复制。由于对象内容包含指针,将造成不良的后果:指针的值被丢弃了(指针内容丢失了也就是说指针地址丢失了,但是改地址中所存储的内容没有丢失,但指针指向的内容并未释放。指针的值被复制了,但是指针所指内容并未复制,这就要出大事儿了)。所以如果类的对象中有指针的话,此处的“=”绝对不能是我们平常理解的简简单单的等于号,这个“=”必须重载,而这个“=”的重载函数就是我们要讨论的赋值构造函数。

    我们再来理解两个概念:值拷贝和位拷贝,位拷贝拷贝的是地址;值拷贝拷贝的是内容。理解了这两个概念我们再看一段代码:
    定义一个string类,但是不实现其他的成员函数

    Class String
    {
    public:
               String(const char *ch = NULL); //默认构造函数
               String(const String &str); //复制构造函数
               ~String();
               String &operator=(const String &str);//赋值构造函数
     privat:
                char *m_data;
    }
    
    int main()
    {
        String strA;
        strA.m_data=L"Windows";
        String strB;
        strB.m_data=L"Linux";
    
        strB.m_data = strA.m_data;
    }

    如果“=”未重写赋值构造函数的话,将strA赋给strB;则编译器会默认进行位拷贝,即strB.m_data = strA.m_data;
    则strA.m_data和strB.m_data都指向了同一块区域,虽然strA.m_data指向的内容会改变为“Linux”,但是会出现这样的问题:
    (1)strB.m_data原来指向的内存区域未释放,造成内存泄露。
    (2)strB.m_data和strA.m_data指向同一块区域,任何一方改变都会影响另一方。
    (3)当对象被析构时,strA.m_data被释放两次。

    如果“=”重写了复制构造函数后,strB.m_data = strA.m_data;进行的是值拷贝,会将strA.m_data的内容赋给strB.m_data,strB.m_data还是指向原来的内存区域,但是其内容改变。

    所以在我理解起来,赋值构造函数其实就是对“=”的重载。缺省的赋值构造函数采用的是“位拷贝”而非“值拷贝”的方式实现。如果类中含有指针变量,那么赋值构造函数如果不重载的话肯定会出错。理解到这里,我们再回过头看一下复制构造函数,其实也是一样的道理,缺省的复制构造函数采用的也是“位拷贝”的方式实现,所以如果类中含有指针变量的话我们也需要重写复制构造函数。
    总而言之一句话,什么把你弄的这么头疼来看这篇文章,归根到底就是——指针。

    最后附上一段不错的理解复制构造函数和赋值构造函数的代码:

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class String  
    {
        public:
            String(const char *str);
            String(const String &other);
            String & operator=(const String &other);
            ~String(void); 
        private:
            char *m_data;
    };
    
    String::String(const char *str)
    {
        cout << "自定义构造函数" << endl;
        if (str == NULL)
        {
            m_data = new char[1];
            *m_data = '\0';
        }
        else
        {
            int length = strlen(str);
            m_data = new char[length + 1];
            strcpy(m_data, str);
        }
    }
    
    String::String(const String &other)
    {
        cout << "自定义复制构造函数" << endl;
        int length = strlen(other.m_data);
        m_data = new char[length + 1];
        strcpy(m_data, other.m_data);
    }
    
    String & String::operator=(const String &other)
    {
        cout << "自定义赋值函数" << endl; 
    
        if (this == &other)
        {
            return *this;
        }
        else
        {
            delete [] m_data;
            int length = strlen(other.m_data);
            m_data = new char[length + 1];
            strcpy(m_data, other.m_data);
            return *this;
        }
    }
    
    String::~String(void)
    {
        cout << "自定义析构函数" << endl; 
        delete [] m_data;
    }
    int main()
    {
        cout << "a(\"abc\")" << endl;
        String a("abc");
    
        cout << "b(\"cde\")" << endl;
        String b("cde");
    
        cout << " d = a" << endl;
        String d = a;
    
        cout << "c(b)" << endl;
        String c(b);
    
        cout << "c = a" << endl;
        c = a;
    
        cout << endl;

    执行结果

    a(“abc”)
    执行自定义构造函数
    b(“ced”)
    执行自定义构造函数
    d=a
    执行自定义复制构造函数
    c(b)
    执行自定义复制构造函数
    c=a
    执行自定义赋值函数

    执行自定义析构函数
    执行自定义析构函数
    执行自定义析构函数
    执行自定义析构函数

    说明几点

    1. 赋值函数中,上来比较 this == &other 是很必要的,因为防止自复制,这是很危险的,因为下面有delete []m_data,如果提前把m_data给释放了,指针已成野指针,再赋值就错了

    2. 赋值函数中,接着要释放掉m_data,否则就没机会了(下边又有新指向了)

    3. 拷贝构造函数是对象被创建时调用,赋值函数只能被已经存在了的对象调用

      注意:String a(“hello”); String b(“world”); 调用自定义构造函数

           String c=a;调用拷贝构造函数,因为c一开始不存在,最好写成String c(a);
      
    展开全文
  • 赋值构造函数只能被已存在的对象调用。 String a(&amp;amp;quot;hello&amp;amp;quot;); String b(&amp;amp;quot;world&amp;amp;quot;); //拷贝构造函数 String c = a; //赋值构造函数 ...

    拷贝构造函数和赋值构造函数声明为私有

    • 默认情况,编译器以“位拷贝”的方式自动生成缺省的构造函数和赋值函数。若类中有指针变量,那么缺省的构造函数会出现错误。
    • 拷贝构造函数在对象创建时被调用
    • 赋值构造函数只能被已存在的对象调用。
    String a("hello");
    String b("world");
    //拷贝构造函数
    String c = a;
    //赋值构造函数
    c= b;
    
    • 拷贝构造函数
    String::String(const String &other)
    {
     int length = strlen(other.m_data);
     m_data = new char[length+1];
     strcpy(m_data, other.m_data);
    }
    
    • 赋值构造函数
    String & String::operate =(const String &other)
    {
     if(this == &other)
     return *this;
     delete [] m_data;
     int length = strlen(other.m_data);
     m_data = new char[length+1];
      strcpy(m_data, other.m_data);
     return *this;
    }
    
    • 如果不想编写拷贝构造、赋值构造函数,又不允许别人使用,可以将拷贝构造函数和赋值构造函数声明为私有函数
    class A
    { 
     private:
     A(const A &a); // 私有的拷贝构造函数
     A & operate =(const A &a); // 私有的赋值函数
    };
    
    A b(a); //错误,不能调用私有的拷贝构造函数
    b = a; //错误,不能调用私有的赋值构造函数
    class B
    {
        public:
           B(A& a)
           : m_a(a) // 错误,不允许调用拷贝构造函数
           {}
        private:
          A m_a; // 错误
    }
    
    class C
    {
        public:
           C(A& a)
           : m_a(a) // 正确,只是引用,并没有拷贝
           {}
        private:
          A& m_a; // 正确
    }
    

    相关系列

    展开全文
  • 昨天晚上在看智能指针的时候,我发现自己连一个拷贝构造函数和赋值构造函数都写不出来,自己就尝试写了一个版本,结果发现错误百出,对于拷贝构造函数和赋值构造函数的理解仅仅停留在理论的方面,而不知其中太多的...

    转载:https://www.cnblogs.com/GODYCA/archive/2013/01/16/2862885.html
    昨天晚上在看智能指针的时候,我发现自己连一个拷贝构造函数和赋值构造函数都写不出来,自己就尝试写了一个版本,结果发现错误百出,对于拷贝构造函数和赋值构造函数的理解仅仅停留在理论的方面,而不知其中太多的内涵。

    比如我们都知道拷贝构造函数和赋值构造函数最大的不同在于:

    拷贝构造是确确实实构造一个新的对象,并给新对象的私有成员赋上参数对象的私有成员的值,新构造的对象和参数对象地址是不一样的,所以如果该类中有一个私有成员是指向堆中某一块内存,如果仅仅对该私有成员进行浅拷贝,那么会出现多个指针指向堆中同一块内存,这是会出现问题,如果那块内存被释放了,就会出现其他指针指向一块被释放的内存,出现未定义的值的问题,如果深拷贝,就不会出现问题,因为深拷贝,不会出现指向堆中同一块内存的问题,因为每一次拷贝,都会开辟新的内存供对象存放其值。

    下面是浅拷贝构造函数的代码:

    复制代码
    #include

    using namespace std;

    class A
    {
    private:
    int* n;

    public:
    A()
    {
    n = new int[10];
    n[0] = 1;
    cout<<“constructor is called\n”;
    }

    A(const A& a)
    {
        n = a.n;
        cout<<"copy constructor is called\n";
    }
    
    ~A()
    {
        cout<<"destructor is called\n";
        delete n;
    }
    
    void get()
    {
        cout<<"n[0]: "<<n[0]<<endl;
    }
    

    };

    int main()
    {
    A* a = new A();
    A b = *a;
    delete a;

    b.get();
    
    return 0;
    

    }
    复制代码
    运行结果如下:

    下面是深拷贝构造函数的代码:

    复制代码
    #include
    #include <string.h>
    using namespace std;

    class A
    {
    private:
    int* n;

    public:
    A()
    {
    n = new int[10];
    n[0] = 1;
    cout<<“constructor is called\n”;
    }

    A(const A& a)
    {
        n = new int[10];
        memcpy(n, a.n, 10);  //通过按字节拷贝,将堆中一块内存存储到另一块内存
        cout<<"copy constructor is called\n";
    }
    
    ~A()
    {
        cout<<"destructor is called\n";
        delete n;
    }
    
    void get()
    {
        cout<<"n[0]: "<<n[0]<<endl;
    }
    

    };

    int main()
    {
    A* a = new A();
    A b = *a;
    delete a;

    b.get();
    
    return 0;
    

    }
    复制代码
    运行截图如下:

    但是赋值构造函数是将一个参数对象中私有成员赋给一个已经在内存中占据内存的对象的私有成员,赋值构造函数被赋值的对象必须已经在内存中,否则调用的将是拷贝构造函数,当然赋值构造函数也有深拷贝和浅拷贝的问题。当然赋值构造函数必须能够处理自我赋值的问题,因为自我赋值会出现指针指向一个已经释放的内存。还有赋值构造函数必须注意它的函数原型,参数必须是引用类型,返回值也必须是引用类型,否则在传参和返回的时候都会再次调用一次拷贝构造函数。

    复制代码
    #include
    #include <string.h>
    using namespace std;

    class A
    {
    private:
    int* n;

    public:
    A()
    {
    n = new int[10];
    n[0] = 1;
    cout<<“constructor is called\n”;
    }

    A(const A& a) //拷贝构造函数的参数一定是引用,不能不是引用,不然会出现无限递归
    {
        n = new int[10];
        memcpy(n, a.n, 10);  //通过按字节拷贝,将堆中一块内存存储到另一块内存
        cout<<"copy constructor is called\n";
    }
    
    A& operator=(const A& a)  //记住形参和返回值一定要是引用类型,否则传参和返回时会自动调用拷贝构造函数
    {
        if(this == &a)     //为什么需要进行自我赋值判断呢?因为下面要进行释放n的操作,如果是自我赋值,而没有进行判断的话,那么就会出现讲一个释放了的内存赋给一个指针
            return *this;
        if(n != NULL)
        {
            delete n;
            n == NULL;   //记住释放完内存将指针赋为NULL
        }
    
        n = new int[10];
        memcpy(n, a.n, 10);
        cout<<"assign constructor is called\n";
        return *this;
    }
    
    ~A()
    {
        cout<<"destructor is called\n";
        delete n;
    

    n = NULL; //记住释放完内存将指针赋为NULL
    }

    void get()
    {
        cout<<"n[0]: "<<n[0]<<endl;
    }
    

    };

    int main()
    {
    A* a = new A();
    A* b = new A();

    *b = *a;
    
    delete a;
    
    b->get();
    

    return 0;
    }
    复制代码
    运行截图如下:

    如果我们在赋值构造函数的形参和返回值不用引用类型,代码如下:

    复制代码
    #include
    #include <string.h>
    using namespace std;

    class A
    {
    private:
    int* n;

    public:
    A()
    {
    n = new int[10];
    n[0] = 1;
    cout<<“constructor is called\n”;
    }

    A(const A& a) //拷贝构造函数的参数一定是引用,不能不是引用,不然会出现无限递归
    {
        n = new int[10];
        memcpy(n, a.n, 10);  //通过按字节拷贝,将堆中一块内存存储到另一块内存
        cout<<"copy constructor is called\n";
    }
    
    A operator=(const A a)   //传参和返回值设置错误
    {
        if(this == &a)
            return *this;
        if(n != NULL)
        {
            delete n;
            n == NULL;
        }
    
        n = new int[10];
        memcpy(n, a.n, 10);
        cout<<"assign constructor is called\n";
        return *this;
    }
    
    ~A()
    {
        cout<<"destructor is called\n";
        delete n;
    }
    
    void get()
    {
        cout<<"n[0]: "<<n[0]<<endl;
    }
    

    };

    int main()
    {
    A* a = new A();
    A* b = new A();

    *b = *a;
    
    delete a;
    
    b->get();
    
    while(1)
    {}
    
    return 0;
    

    }
    复制代码
    运行截图如下:

    多了两次的拷贝构造函数的调用和两次析构函数的调用。

    展开全文
  • 最近老是有人问我拷贝构造函数和赋值构造函数。还老师
  • 赋值构造函数和拷贝构造函数 常见的给对象赋值方式有构造函数,拷贝构造函数,赋值运算符这三种方法,如下代码演示了这几种常见的方法。 Tan a1; (1) Tan a2(a1);
  • 参考:C++ 拷贝构造函数和赋值构造函数 一、简单说明 在C++中,如果我们定义一个空类,编译器会默认为我们自动生成以下四个函数: 1、默认构造函数 2、析构函数 3、拷贝构造函数 4、赋值构造函数。通常情况下,我们...
  • C++ 拷贝构造函数 赋值构造函数拷贝构造函数和赋值构造函数的异同由于并非所有的对象都会使用拷贝构造函数和赋值函数,程序员可能对这两个函数有些轻视。请先记住以下的警告,在阅读正文时就会多心:如果不主动...
  • 标题:String函数的实现-->主要实现构造函数,拷贝构造函数,析构函数,赋值构造函数。这几个函数是字符串函数最基本的函数,今天也总结一下 #include using namespace std; #include class MyString { private: ...
  • 在C++如果设置拷贝构造函数和赋值构造函数不可用呢, 其实很简单,只需把它们设置为private就可以了,如下所示: class Thing  { public: private: Thing(const Thing&); Thing& operator=(const Thing&);...
  • C++ 赋值构造函数 复制构造函数

    千次阅读 2018-08-25 15:29:11
    默认构造函数 编译器提供一个不接受任何参数,也不执行任何操作的构造函数,称之为默认构造函数 这是因为创造对象的时候总会调用默认构造函数 Klunk::Klunk() {} //定义 Klunk lunk; //声明 使用默认构造函数 ...
  • C++ 拷贝构造函数和赋值构造函数

    万次阅读 多人点赞 2019-04-06 16:43:35
    在C++中复制控制是一个比较重要的话题,主要包括复制构造函数、重载赋值操作符、析构函数这三部分,这三个函数是一致的,如果类需要析构函数,则它也需要复制操作符 和 复制构造函数,这个规则被称为 C++的“三法则...
  • 拷贝构造函数和赋值构造函数的异同 由于并非所有的对象都会使用拷贝构造函数和赋值函数,程序员可能对这两个函数有些轻视。请先记住以下的警告,在阅读正文时就会多心:如果不主动编写拷贝构造函数和赋值函数,...
  • 什么时候需要拷贝构造函数和赋值构造函数? 每个C++类只有一个析构函数,但可以有多个构造函数和多个赋值函数。其中构造函数包括默认构造函数(无参,或参数全有默认值),拷贝构造函数。在编程时,如果程序员不...
  • 拷贝构造函数与赋值构造函数(operator=)的区别 拷贝构造函数是创建一个不存在的对象 赋值构造函数是将一个对象赋值给另一个已经存在的对象。 ...
  • 赋值构造函数和拷贝构造函数的区别 复制构造函数 《c++ Primer Plus(第五版)中文版》中 第385页中写道: 假设motto是一个StringBad对象,则下面4中声明都将是调用复制构造函数(及拷贝构造函数)...
  • 我之所以认为赋值构造函数不应该被称为一个构造函数,是因为在调用赋值构造函数的时候,类对象已经存在,自然谈不上构造类对象,它只是对该已经存在的类对象的成员变量进行修改(所谓的赋值)操作。而拷贝构造函数是...
  • C++拷贝构造函数和赋值构造函数

    千次阅读 2011-02-20 20:36:00
    C++拷贝构造函数和赋值构造函数 简介:这是C++拷贝构造函数和赋值构造函数的详细页面,介绍了和c/c++,有关的知识、技巧、经验,和一些c/c++源码等。 重点:包含动态分配成员的类 应提供拷贝构造函数,并...
  • 什么时候用拷贝构造函数,和赋值构造函数: (一)当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被...
  • 对于拷贝构造函数以及赋值构造函数的定义,我就不再啰嗦了,还是给出一个简单的例子,更直观些吧。 class CStr { public: CStr(); //默认构造函数 CStr(const char* psz); //一种广义拷贝构造函数,不过也有人...
  • 什么时候用拷贝构造函数,和赋值构造函数:    (一)当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 456,060
精华内容 182,424
关键字:

赋值构造函数