c++学习笔记--构造函数、析构函数、拷贝构造函数

说在前面

今天总结一下c++的构造函数与析构函数知识点

1、类和对象

在说构造函数之前必须先说下C语言中类和对象

一、类的声明
有两种方式声明类,一种使用class来声明,一种使用struct来声明

class 【类名称】
{
	数据和方法的定义(可能含有类型成员)
};

struct 【类名称】
{
	数据和方法的定义(可能含有类型成员)
};

二、类的封装
这两者都可以用来声明一个对象,但是其不同之处在于封装形式;
class 定义的类默认的封装是private ,private指成员变量是私有的,只能在本类内部进行访问
struct 定义的类默认的封装是public(因为struct要兼容C),C++常用的是class ,public 代表成员变量是公有的,可以在类的外部进行使用

public(共有) protected(保护) private(私有)

在这里插入图片描述在这里插入图片描述
三、成员函数与this指针
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。

成员函数:
①类外定义成员函数必须包含域名 ,使用范围解析运算符 ::
②在调用时必须加()要不然会报错
③成员函数也满足封装的特性
④成员函数的区域是整个类的内部区域

this指针,在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。

this指针:
① 对于成员变量的访问时隐式用了this指针 , 平时操作不用显示
②this指针指向调用函数的对象

四、Inline声明
在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。

定义在类中的成员函数默认都是内联的,如果在类定义时就在类内给出函数定义,那当然最好。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的。

inline 的使用是有所限制的,inline 只适合涵数体内代码简单的涵数使用,不能包含复杂的结构控制语句例如 while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)

类内声明, 相当于隐式加了inline关键字,
对于频繁调用而逻辑简单的函数可用 inline标记
类外声明, 如果要加inline标记,必须显式声明

2、构造函数

一、定义
如果没有显示定义则编译器自动合成一个默认构造函数, 构造函数的名称与类名相同,没有返回值; 如果定义了一个非默认的构造函数,系统不会合成默认构造函数, 没有默认的构造函数,需显示生成对象

二、构造函数的初始化列表
参数的初始化, 在构造函数的后面加 :【成员变量名】(值)
参数的赋值, 直接在构造函数里面进行赋值
1、成员初始化列表作用在初始化阶段对于基本类型来说,初始化和赋值没什么区别。对于类对象:初始化列表表示了调用哪一个构造函数,不写初始化列表表示调用默认。
2、初始化阶段完成后,执行构造函数体

三、拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

通过使用另一个同类型的对象来初始化新创建的对象。
复制对象把它作为参数传递给函数。
复制对象,并从函数返回这个对象。

如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数

复制构造函数

 Person( const Person &p):age(p.age)// 在参数列表对形参进行赋值
{
cout <<''copy构造函数"<<endl;
}

3、析构函数

一、定义
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

二、 说明
析构函数以~起头后接类名
析构函数不能带参数
析构函数不能重载
析构函数不能显示调用
对于动态生成的对象需要使用delete关键字调用析构函数
如果没有显式定义析构函数,则编译器会自动生成一个析构函数
如果成员变量是动态申请内存,必须显示定义析构函数,并进行内存释放


#include <iostream>
 
using namespace std;
 
class Line
{
private:
    int *ptr;
public:
 // 成员函数定义,包括构造函数
    Line(int len)
    {
        cout << "调用构造函数" << endl;
        // 为指针分配内存
        ptr = new int;
        *ptr = len;
    }
    // 拷贝构造函数
    Line(const Line &obj)
    {
        cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
        ptr = new int;
        *ptr = *obj.ptr; // 拷贝值
    }
    // 析构函数
    ~Line(void)
    {
        cout << "释放内存" << endl;
        delete ptr;
    }
    int getLength( void )
    {
        return *ptr;
    }
};

void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}
 
// 程序的主函数
int main( )
{
    Line line1(10);
    Line line2 = line1; // 这里也调用了拷贝构造函数
 
    display(line1);
    display(line2);
 
    return 0;
}

运行结果:

在这里插入图片描述

从上面的程序中就可以看出来,构造函数,析构函数,拷贝构造函数的运行情况

补充拷贝构造函数:

1、必须定义拷贝构造函数的情况:

只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义拷贝构造函数也可以拷贝;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义拷贝构造函数。

2、什么情况使用拷贝构造函数:

类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
(1)一个对象以值传递的方式传入函数体
(2)一个对象以值传递的方式从函数返回
(3)一个对象需要通过另外一个对象进行初始化。

3、关于为什么当类成员中含有指针类型成员且需要对其分配内存时,一定要有总定义拷贝构造函数??

默认的拷贝构造函数实现的只能是浅拷贝,即直接将原对象的数据成员值依次复制给新对象中对应的数据成员,并没有为新对象另外分配内存资源。这样,如果对象的数据成员是指针,两个指针对象实际上指向的是同一块内存空间。

在某些情况下,浅拷贝回带来数据安全方面的隐患。

当类的数据成员中有指针类型时,我们就必须定义一个特定的拷贝构造函数,该拷贝构造函数不仅可以实现原对象和新对象之间数据成员的拷贝,而且可以为新的对象分配单独的内存资源,这就是深拷贝构造函数。

4、如何防止默认拷贝发生

声明一个私有的拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类的对象,编译器会报告错误,从而可以避免按值传递或返回对象。

总结:

当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。所以,这时,必须采用深拷贝。
深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值