C++拷贝构造函数中的陷阱

转自微信公众号:CPP开发前沿

拷贝构造函数大家都比较熟悉,通俗讲就是传入一个对象,拷贝一份副本。
不过看似简单的东西,实际不注意的话就会产生问题!

#include<iostream>
using namespace std;
class CExample
{
public:
int a,b,c;
char *str;
public:
//构造函数
    CExample(int tb)
    {
        a = tb;
        b = tb+1;
        c = tb+2;
        str=(char *)malloc(sizeof(char)*10);
strcpy(str,"123456789");
cout<<"creat: "<<endl;
    }
 
//析构函数
    ~CExample()
    {
cout<< "delete: "<<endl;
    }
void Show ()
{
cout<<a<<endl;
    }
//拷贝构造
//CExample(const CExample& C)
//{
//    str=(char *)malloc(sizeof(char)*10);
//    strcpy(str,C.str);
//    cout<<"copy"<<endl;
//}
};
//全局函数,传入的是对象
void g_Fun(CExample C)
{
    C.a=0;C.b=0;C.b=0;
strcpy(C.str,"aaabbbccc");
cout<<"test"<<endl;
}
int main()
{
CExample test(1);
cout<<"str:"<<test.str<<" a="<<test.a<<" b="<<test.b<<" c="<<test.c<<endl;
    g_Fun(test);//传入对象
cout<<"str:"<<test.str<<" a="<<test.a<<" b="<<test.b<<" c="<<test.c<<endl;
    getchar();
return 0;
}

这个结果似乎出乎了我们的预料,作为形式参数 test对象被修改了,同时是test.str的部分被修改了,test的整数成员变量没有被修改!

咱们先了解一下系统默认的拷贝构造函数,因为类中没有写自己的拷贝构造函数,所以调用的是默认的拷贝构造函数。

Thinking in c++:对于简单结构,编译器会自动生成一个缺省的,就是位拷贝(bitcopy)。

对于比较复杂的类型,编译器就会自动生成一个缺省的拷贝构造函数。

class CExample
{
int a,b,c;
};

这就是一个简单结构的类,位拷贝,就是按对象在内存中的二进制进行拷贝,对于不涉及指针等类型的时候,位拷贝是比较不错的拷贝方法。

但是,要是一个类中有指针类型的时候,如:

class CExample
{
int a,b,c;
char *str;
};

位拷贝就会把指针地址拷贝了一下,话句话说,这里只进行了“浅拷贝”,一旦副本里涉及到指针的操作,必然就会影响到原始对象的成员变量,这就是导致,上面代码中对象的整数变量没被修改(对整数变量的位拷贝其实就是一种“深拷贝”),而str所指的对象被修改的原因。

那么该如何防止对副本的修改影响原始对象呢?

答案是用户自定义拷贝构造函数!

CExample(const CExample& C)
{
    a=C.a;b=C.b;c=C.b;
    str=(char *)malloc(sizeof(char)*10);
strcpy(str,C.str);
cout<<"copy"<<endl;
}

这样就可以正确完成拷贝构造的操作了。

总结:对于简单的数据类型,可以使用系统默认的拷贝构造函数;但对于复杂的数据类型(如指针),其实就是深拷贝和浅拷贝的区别!一般类如果包含指针或引用成员,应该遵守Rule of Three原则。

@24K纯开源 指出的三法则:
三法则(英语:rule of three,the Law of The Big Three,The Big Three;三法则,三大定律)在 C++ 程序设计里,它是一个以设计的基本原则而制定的定律,三法则的要求在于,假如类型有明显地定义下列其中一个成员函数,那么程序员必须连其他二个成员函数也一同编写至类型内,亦即下列三个成员函数缺一不可。

析构函数(Destructor

复制构造函数(copy constructor

复制赋值运算符(copy assignment operator

到此这篇关于C++拷贝构造函数中的陷阱的文章就介绍到这了,更多相关拷贝构造函数陷阱内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++构造函数一些常见的坑

    文章转自微信 公众号:Coder梁(ID:Coder_LT) 某一天我们接到了一个需求,需要开发一个类似于STL中string的类. 我们很快写好了代码: #include <iostream> #ifndef STRINGBAD_H_ #define STRINGBAD_H_ class StringBad {     private:      char *str;      int len;      static int num_strings;     public:      St

  • C++构造函数详解

    文章转自公众号:Coder梁(ID:Coder_LT) 上一篇文章我们介绍了定义了类,在使用之前,往往还需要对类进行初始化.这篇介绍的就是对类进行初始化的方法. 像是结构体,我们可以使用列表初始化的方法进行初始化: struct Thing {     char *pn;     int m; }; Thing th = {"hello", 23}; 但类不行,因为结构体当中的成员变量都是public的,而类往往是私有的.这意味着我们不能直接用程序访问数据成员,需要设计成函数. 在C+

  • c++特殊构造函数详解

    目录 前言 拷贝构造函数 一.什么是拷贝构造函数 二.调用时机 注意 浅拷贝和深拷贝 总结 前言 众所周知,构造函数的作用是类在创建对象时的初始化,而拷贝构造函数则是构造函数里的一种特殊构造. 拷贝构造函数 拷贝构造:是C++特有的,他是一种特殊的构造函数 用于基于一个同一个类的的第一个对象去创造和初始化的一个对象 第一的参数是本类的对象的引用(const) 一.什么是拷贝构造函数 一种特殊的构造函数,同一个类的一个对象去创造或初始化一个对象 在没写的时候,是默认存在的,自己写了之后,系统默认的

  • 详解C++构造函数

    目录 1.作用 2.代码举例 2.1 示例1: 2.2 示例2: 3. 使用 3.1 使用构造函数初始化 3.2 有参数的构造函数 3.3 默认的构造函数 4. 成员初始化列表 例1:正常初始化 例2:成员初始化列表 为啥推荐成员初始化列表的写法? 总结 1.作用 一种特殊类型的方法,在每次实例化对象时运行 2.代码举例 2.1 示例1: #include <iostream> class A { public: float a, b; void print() { std::cout <

  • C++编程析构函数拷贝构造函数使用示例详解

    目录 构造函数 析构函数 拷贝构造之深拷贝和浅拷贝 深浅拷贝区别 首先定义一个类进行操作. class MM { public: protected: int year; string name; } 构造函数在类中默认有一个无参的构造函数 默认的构造函数为 类名(){}:这个构造函数 如果直接写了构造函数那么这个构造函数将会没有 构造函数 class MM { public: //MM() {};//无参构造函数 MM(int year, string name) :year(year), n

  • C++构造函数的初始化列表详解

    目录 1.问题 2.解决方法(初始化列表) 3.顺序问题 总结 1.问题 class A { private: int m_a; public: A(int a) { cout << "A(int a)......." << endl; m_a = a; } void print() { cout <<"m_a=" << m_a << endl; } }; class B { private: int m_

  • C++拷贝构造函数中的陷阱

    转自微信公众号:CPP开发前沿 拷贝构造函数大家都比较熟悉,通俗讲就是传入一个对象,拷贝一份副本.不过看似简单的东西,实际不注意的话就会产生问题! #include<iostream> using namespace std; class CExample { public: int a,b,c; char *str; public: //构造函数     CExample(int tb)     {         a = tb;         b = tb+1;         c =

  • c++中拷贝构造函数的参数类型必须是引用

    在C++中, 构造函数,拷贝构造函数,析构函数和赋值函数(赋值运算符重载)是最基本不过的需要掌握的知识. 但是如果我问你"拷贝构造函数的参数为什么必须使用引用类型?"这个问题, 你会怎么回答? 或许你会回答为了减少一次内存拷贝? 很惭愧的是,我的第一感觉也是这么回答.不过还好,我思索一下以后,发现这个答案是不对的. 原因:如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传

  • C++中拷贝构造函数的使用

    目录 拷贝构造函数 1. 手动定义的拷贝构造函数 2. 合成的拷贝构造函数 总结 拷贝构造函数 拷贝构造函数,它只有一个参数,参数类型是本类的引用.复制构造函数的参数可以是 const 引用,也可以是非 const 引用. 一般使用前者,这样既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数去初始化其他对象.一个类中写两个复制构造函数,一个的参数是 const 引用,另一个的参数是非 const 引用,也是可以的. 1. 手动定义的拷贝构造函数 Human.h #pra

  • php中拷贝构造函数、赋值运算符重载

    对象的赋值与复制: 赋值:通过" = "运算符重载User a(10),b;b = a;复制:调用复制构造函数User b;User a(b);或者User a = b;//相当于User a(b);与赋值的区别,赋值是对一个已经存在的对象进行赋值(已经实现定义了被赋值的对象),而复制是从无到有建立一个新的对象,并使它与已有的对象相同.浅复制与深复制: 若对象中有指针成员,在复制时,只会将该指针成员的地址复制给新建立的对象,因此,两个对象中的指针成员都指向了同一块内存区域,在释放时会出

  • C++中拷贝构造函数的总结详解

    1.什么是拷贝构造函数: 拷贝构造函数嘛,当然就是拷贝和构造了.(其实很多名字,只要静下心来想一想,就真的是顾名思义呀)拷贝又称复制,因此拷贝构造函数又称复制构造函数.百度百科上是这样说的:拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化.其唯一的参数(对象的引用)是不可变的(const类型).此函数经常用在函数调用时用户定义类型的值传递及返回. 2.拷贝构造函数的形式 复制代码 代码如下: Class X{public: X(); X(const

  • C++中拷贝构造函数的应用详解

    一.C++中拷贝构造函数的定义: 有一个参数的类型是其类类型的构造函数是为拷贝构造函数. 如下所示: X::X( const X& x); Y::Y( const Y& y, int =0 ); //可以是多参数形式,但其第二个即后继参数都有一个默认值 二.拷贝构造函数的应用: 当一个类对象以另一个同类实体作为初值时,大部分情况下会调用拷贝构造函数. 一般是这三种具体情况: 1.显式地以一个类对象作为另一个类对象的初值,形如X xx=x; 2.当类对象被作为参数交给函数时. 3.当函数返回

  • 深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结

    1 . 用同一个类的源对象构造一个目标对象时,会调用拷贝构造函数来构造目标对象,如果没有定义拷贝构造函数,将调用类的默认拷贝函数来构造目标对象.2 . 当一个函数的返回值为一个类的对象时,如果在调用函数中,没有定义一个对象来接收这个返回对象值,会用返回一个临时对象保存返回对象的值.在被调用函数结束时,这个临时对象被销毁.而当调用函数中有一个接受对象时,就将返回对象赋值给接收对象,这个返回对象在调用函数结束时调用析构函数.3. 当类有一个带有一个参数的构造函数时,可以用这个参数同类型的数据初始化这

  • 详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现

    C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存.(构造函数的命名必须和类名完全相同) 首先说一下一个C++的空类,编译器会加入哪些默认的成员函数 默认构造函数和拷贝构造函数 析构函数 赋值函数(赋值运算符) 取值函数 **即使程序没定义任何成员,编译器也会插入以上的函数! 注意:构造函数可以被重载

  • C++中的拷贝构造函数详解

    目录 C++拷贝构造函数(复制构造函数)详解 1) 为什么必须是当前类的引用呢? 2) 为什么是 const 引用呢? 默认拷贝构造函数 总结 C++拷贝构造函数(复制构造函数)详解 拷贝和复制是一个意思,对应的英文单词都是copy.对于计算机来说,拷贝是指用一份原有的.已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据.例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的图片拷贝到桌面以方便浏览,将重要的文件上传到百度网盘以防止丢失等,都是「创建一份新数据」的意思. 在

  • 详解C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义,但是也没有显式的删除),编译器会自动的隐式生成一个拷贝构造函数和赋值运算符.但用户可以使用delete来指定不生成拷贝构造函数和赋值运算符,这样的对象就不能通过值传递,也不能进行赋值运算. class Person { public: Person(const Person& p) = dele

随机推荐