c++基础概念之深浅拷贝

0.拷贝构造函数

其实就是类与类之间的赋值操作所要调用的函数(因为类对象与普通对象不同,类对象内部结构较为复杂,存在各种成员变量)

代码举例:

#include<iostream>
using namespace std;

class Myclass {  
private:  
    int a;  
public:  
    //构造函数  
    Myclass(int b)  
    { a = b;}  

    //拷贝构造函数  
    Myclass(const Myclass& C)  
    {  
        a = C.a;  
    }  

    //一般函数  
    void Show ()  
    {  
        cout<<a<<endl;  
    }  
};  
int main()  
{  
    Myclass A(100);  
    Myclass B = A; //Myclass B(A)也是一样的,调用拷贝构造函数,并不是简单的赋值
    B.Show ();  
    return 0;  
}   

拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。

1.深浅拷贝

浅拷贝

在对象赋值时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝

代码举例:

#include<iostream>
using namespace std;
class Array{
    public:
    Array(int t)
    {
        cout << "Array" << endl ;
        count = t ;
    }
    Array(const Array &temp) //尽可能的使用 const 
    {
        cout << "~Array" << endl ;
        count = temp.count ;
    }
    void print()
    {
        cout << "count == " << count << endl;
    }
    private:
    int count;
};
int main(void)
{
    Array My1(5);
    Array My2 = My1;
    My1.print();
    My2.print();
}

运行结果:

这里写图片描述

深拷贝

先来看下面的一个例子:

#include<iostream>
using namespace std;
class Array{
    public:
    Array(int t)
    {
        cout << "Array" << endl ;
        count = t ;
        p = new int[count];
        for(int i= 0;i != count ;++i)
            p[i] = i*i ;
    }
    Array(const Array &temp)  //尽可能的使用 const 
    {
        cout << "~Array" << endl ;
        count = temp.count ;
        p = new int[temp.count];
        p = temp.p;   //思考一下这里对不对?为什么不对?会发生什么?
    }
    void print()
    {
        cout << "count == " << count << endl;
        for(int i =0;i != count ;++i)
            cout << p[i] << endl ; //公有可以访问私有
    }
    void printAddr()
    {
        cout << "p == " << p << endl ;
    }
    ~Array()
    {
        delete []p;
        p= NULL;
    }
    private:
    int count;
    int *p  ;  
};
int main(void)
{
    Array My1(5);
    Array My2 = My1;
    My1.printAddr();
    My2.printAddr();
    My1.print();
    My2.print();
}

运行截图:

这里写图片描述

可以很明显的看到图中的报错“double free”。说明我们将一块内存 free了两次。产生了错乱!那么是什么原因引起的呐?

没错就是我们的拷贝构造函数,当我们写为p = temp.p; 就是将一个对象的p指针指向了传进来的temp.p的地址。那么他们两个对象的指针就指向了同一块内存,之后在析构函数中被free时,就会产生double free 。那么如何解决这个问题呐?

就是将传进来的temp 的每一块内存分别赋值给p,于是拷贝构造函数就成了这样:

    Array(const Array &temp)
    {
        cout << "~Array" << endl ;
        count = temp.count ;
        p = new int[temp.count];
        //p = temp.p;  是两个指针指向了同一块地址,delete 了两次
        for(int i =  0 ;i< temp.count ;++i)
            p[i] = temp.p[i];
    }

运行结果:

这里写图片描述

没错,这就是我们的深拷贝啦~_~

参考学习:参考学习

2.对象成员指针

所谓的对象成员指针就是指在一个类的成员中出现了另一个类的指针。

代码举例:

#include<iostream>
using namespace std;
class coordinate { //坐标点
public:
    coordinate(int x,int y)
    {
        this->x= x;
        this->y =y;
    }
    ~coordinate()
    {
    }
    int getX()
    {
        return x;
    }
    int getY()
    {
        return y;
    }
private:
    int x;
    int y;
};

class Line{  //许多个坐标点组成线段
public:
    Line(int x1 ,int y1,int x2,int y2)
    {
        A = new coordinate(x1,y1);
        B = new coordinate(x2,y2);
    }
    void print()
    {
        cout << A->getX() << " " << A->getY() << endl ;
        cout << B->getX() << " " << B->getY() << endl ;
    }
    ~Line()
    {
        delete A;
        A=NULL;
        delete B;
        B=NULL;
    }
private:
    coordinate *A; //对象成员指针
    coordinate *B;
};
int main(void)
{
//    Line *p= new Line(1,2,3,4);
    Line *p;
    int *t;
    p= new Line(1,2,3,4);
    p->print();
    delete p ;
    p= NULL;
    cout <<"   t  == " <<  sizeof(t)<< endl ;
    cout <<"   p  == " <<  sizeof(p)<< endl ;
    cout <<"   sizeof(line1) == " <<  sizeof(Line)<< endl ;
    return 0;
}

运行截图:

这里写图片描述

PS:不知道大家有木有注意到 sizeof 什么什么的那部分,在 64 位的机器中,一个指针占8个字节,那么请想上一想?为什么sizeof(line1)输出的是16呐?因为line对象中有两个指针coordinate 呀。哈哈哈

3.常对象成员与函数

常对象成员:就是用 const 来修饰的对象的数据成员对象,初始化时就只能用初始化列表来完成初始化。常成员函数就是用const来修饰成员函数,那么常成员函数中就不能修改数据成员的值了,因为:

void play() const  //常成员函数的定义
{
    x= 10;
}

就等价于下面:

void play(const Myclass *this)
{
    this->x = 10;  //!!!!
}

显然给一个 const指针指向的数据成员赋值自然是错误的。还有:

void play() const;
void play() ;

两个函数互为重载,但是这个只能说可以这样用,但绝对不会有人这样使用!!!!

如何调用常成员函数:

代码举例:

int main(void)
{
    const Myclass Myclass(4);//常对象就会调用常成员函数
}

PS:一般使用初始化列表就三种情况,

1.const成员的初始化
2.有参构造的对象成员
3.引用成员的初始化

4.常指针与常引用

就是下面的这两个东东:

    const coordinate  &My2 = My1;  //常引用
    const coordinate  *p = &My1;   //常指针

说明:与常对象相同,只能使用常成员函数,不能使用其他类型的函数

代码举例:

#include<iostream>
using namespace std;
class coordinate {
public:
    coordinate(int x,int y)
    {
        this->x= x;
        this->y =y;
    }
    ~coordinate()
    {
    }
    int getX()  //与下面的是不同的函数,但一般不这样书写
    {
    }
    int getX() const  //常成员函数,与上面的函数不冲突,但不建议这样写
    {
        return x;
    }
    int getY() const 
    {
        return y;
    }
    void printInfo() const  //要求 this 指针只读
    {
        cout << "liushengxi ----- " << endl;
    }
private:
    int x;
    int y;
};
int main(void)
{
    coordinate   My1(1,2); 

    const coordinate  &My2 = My1; //常引用
    const coordinate  *p = &My1; //常指针

    My2.getX(); //getX 要求this 是一个具有读写权限的对象,下同
    p->getY();

   return 0;
}
原文地址:https://www.cnblogs.com/Tattoo-Welkin/p/10335317.html