【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南

一、类的引出概述

        在c语言结构体中,行为和属性是分开的,万一调用错误,将会导致问题发生。c++中类将数据方法封装在一起,加以权限区分,用户只能通过公共方法 访问 私有数据

二、封装

        封装特性包含两个方面,一个是属性和变量合成一个整体,一个是给属性和函数增加访问权限

1.把变量(属性)和函数(操作)合成一个整体,封装在一个类中

2.对变量和函数进行访问控制访问权限
3.在类的内部(作用域范围内),没有访问权限之分,所有成员可以相互访问

4.在类的外部(作用域范围外),访问权限才有意义:.public、private、protected

5.在类的外部,只有public.修饰的成员太能被访问,在没有涉及继承与派生时,private和protected是相同等级的.外部不允许访问        

访问属性 属性 对象内部 对象外部
public 公有 可访问 可访问
private 保护 可访问 不可访问
protected 私有 可访问 不可访问

尽量设置成员变量为私有权限,将方法设置成公有。

优点:

对变量的设置时的控制

实现变量设置只读权限

实现变量设置只写权限

实现变量设置可读可写权限

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第1张图片

struct和class的区别:class默认访问权限为.private,struct默认访问权限为public.

三、类的案例(了解)

1、设计一个person类

        设计一个Person类,Person类具有name和age属性,提供初始化函数(Init),并提供对name和age 的读写函数(set,get),但必须确保age的赋值在有效范围内(0-100),超出有效范围,则拒绝赋值,并 提供方法输出姓名和年龄。----需要六个函数

#include 
using namespace std;
#include
class Person
{
private:
    char mName[32];
    int mAge;
public:
    //初始化成员
    void init(char *name, int age)
    {
        strcpy(mName, name);
        if(age>=0 && age<=100)
        {
            mAge = age;
        }
        else
        {
            cout<<"年龄无效"<=0 && age<=100)
        {
            mAge = age;
        }
        else
        {
            cout<<"年龄无效"<

2、设计一个立方体类

        设计立方体类(Cube),求出立方体的面积( 2ab +2ac +2bc )和体积( a*b*c),分别用全局函数和成员函数判断两个立方体是否相等。

class Cube
{
private:
    int mA;
    int mB;
    int mC;
    public:
    void setA(int a)
    {
        mA = a;
    }
    int getA(void)
    {
        return mA;
    }
    void setB(int b)
    {
        mB = b;
    }
    int getB(void)
    {
    return mB;
    }
    void setC(int c)
    {
        mC = c;
    }
    int getC(void)
    {
        return mC;
    }
    //获取面积
    int getS(void)
    {
        return (mA*mB+mB*mC+mC*mA)*2;
    }
    //获取体积
    int getV(void)
    {
        return mA*mB*mC;
    }
    //成员函数 比较两个立方体是否先等
    bool compareCube01(Cube &ob)
    {
        if(mA==ob.mA && mB ==ob.mB && mC == ob.mC)
    {
        return true;
    }
        return false;
    }
};

//全局函数 比较两个立方体是否先等
bool compareCube02(Cube &ob1, Cube &ob2)
{
    if(ob1.getA()==ob2.getA() && ob1.getB() ==ob2.getB() && ob1.getC() == ob2.getC())
    {
        return true;
    }
        return false;
}
    void test()
{
    Cube ob1;
    ob1.setA(10);
    ob1.setB(20);
    ob1.setC(30);
    cout<<"面积:"<

3、点和圆的关系

        设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。

class Point
{
private:
    int mX;
    int mY;
public:
    void setX(int x)
    {
        mX = x;
    }
    int getX(void)
    {
        return mX;
    }
    void setY(int y)
    {
        mY = y;
    }
    int getY(void)
    {
        return mY;
    }
};
class Circle
{
private:
    Point p;//对象作为类的成员变量
    int mR;
public:
    void setPoint(int x, int y)
    {
        p.setX(x);
        p.setY(y);
    }
    Point getPoint(void)//打印圆点
    {
        return p;
    }
    void setR(int r)
    {
        mR = r;
    }
    int getR(void)
    {
        return mR;
    }
    //判断点 在圆的位置
    int pointIsOnCircle(Point &ob)
    {
        int len = (ob.getX()-p.getX())*(ob.getX()-p.getX())+\
        (ob.getY()-p.getY())*(ob.getY()-p.getY());
        if(len == mR*mR)
        {
            return 0;
        }
        else if(len > mR*mR)
        {
            return 1;
        }
        else if(len < mR*mR)
        {
            return -1;
        }
    }
};
void test()
{
//实例化一个点的对象
    Point p;
    p.setX(5);
    p.setY(5);
    //实例化一个圆的对象
    Circle cir;
    cir.setPoint(2,2);
    cir.setR(5);
    if(cir.pointIsOnCircle(p) == 0)
    {
        cout<<"点在圆上"< 0)
    {
        cout<<"点在圆外"<

4、类中成员函数在类外实现

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第2张图片

5、类在其他文件实现

        头文件定义类, cpp实现类的成员函数

data.h

#ifndef DATA_H
#define DATA_H
class Data
{
private:
    int mA;
public:
    int getA(void);
    void setA(int a);
};
#endif 

 data.cpp

#include "data.h"
int Data::getA()
{
    return mA;
}
void Data::setA(int a)
{
    mA = a;
}

main.cpp

#include 
#include "data.h"
using namespace std;
int main(int argc, char *argv[])
{
    Data ob;
    ob.setA(100);
    cout<

对象的构造和析构:

        当我们创建对象的时候,这个对象应该有一个初始状态,当对象销毁之前应该销毁自己创建的一些数据。对象的初始化清理也是两个非常重要的安全问题,C++为了给我们提供这种问题的解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用完成对象初始化(创建对象时为对象成员属性赋值)和对象清理工作。初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事。

四、构造函数(初始化工作)

1、概述

        构造函数是类实例化对象时自动调用

2、创建

        构造函数名与类名称相同,不能有返回值类型(连void都不可以),可以有参数(支持重载),必须加public权限。

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第3张图片

3、分类

        无参构造、有参构造。

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第4张图片

4、调用

        类实例化对象时:先为对象开辟空间 然后才调用构造函数。 

1、如果用户不提供构造函数 编译器会自动 提供一个无参空的构造函数。

2、如果用户提供构造函数 编译器会自动 屏蔽默认无参的构造

四种:隐式调用、显示调用、隐式转换、匿名调用 

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第5张图片

注:写任何一个类 无参构造, 有参构造都需要实现 

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第6张图片

5、explicit关键字防止构造函数隐式转换

        explicit修饰构造函数,防止构造函数隐式转换,避免令人产生赋值误会。

允许隐式转换:

//此时允许有参构造隐式转换
A(int a)
{
    mA = a;
    cout<<"A的有参构造mA="<
//构造函数隐式转换(类中只有一个数据成员)
A ob1=100;//ok

不允许隐式转换:

//防止有参构造 隐式转换
explicit A(int a)
{
    mA = a;
    cout<<"A的有参构造mA="<
//构造函数隐式转换(类中只有一个数据成员)
A ob1=100;//err 转换失败

五、析构函数(清理工作)

         当对象生命周期结束的时候 系统自动调用析构函数。

        函数名和类名称相同,在函数名前加~,没有返回值类型,没有函数形参。(不能被重载) 先调用析构函数 再释放对象的空间。

调用释放顺序:括号内的最先释放,释放先进后出

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第7张图片

         一般情况下,空的析构函数就足够。但是如果一个类有指针成员,这个类必须 写析构函数,释放指针成员所指向空间。

#include
#include
#include
using namespace std;
class Data
{
public:
	char *name;
public:
	Data()
	{
		name = NULL;
	}
	Data(char *str)
	{
		name = (char*)calloc(1, strlen(str) + 1);
		strcpy(name, str);
		cout << "有参构造 name=" << name << endl;
	}
	~Data()
	{
		cout << "析构函数" <

六、拷贝构造函数

1、定义

        拷贝构造函数本质是构造函数

        拷贝构造的调用时机:对象 初始化 对象 时。

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第8张图片

 如果用户不提供拷贝构造 编译器会自动提供一个默认的拷贝构造(完成赋值动作--浅拷贝)

2、拷贝构造 和 无参构造 有参构造的关系

如果用户定义了 拷贝构造或者有参构造 都会屏蔽无参构造。

如果用户定义了 无参构造或者有参构造 不会屏蔽拷贝构造。

3、拷贝构造调用形式

(1)旧对象给新对象初始化 调用拷贝构造

Data ob1(10);
Data ob2 = ob1;//调用拷贝构造

(2)普通对象作为函数参数 调用函数时 会发生拷贝构造 

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第9张图片

(3)函数返回值普通对象 (Visual Studio会发生拷贝构造) 

Data get(void)
{
	Data ob1(10);
	return ob1;
}
void test()
{	
	Data ob2 = get();
}

注:给对象取别名 不会调用拷贝构造 

Data ob1(10);
Data &ob2 = ob1;//不会调用拷贝构造

4、拷贝构造中浅拷贝和深拷贝

        如果类中没有指针成员, 不用实现拷贝构造和析构函数。

        如果类中指针成员和拷贝构造调用, 必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作(因拷贝构造释放时相应堆区空间需再释放一次,故堆区空间也需重新拷贝一次)

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第10张图片

 七、初始化列表

成员对象:一个类的对象 作为另一个类的成员

类会自动调用成员对象的无参构造,如果类中想调用成员对象有参构造 必须使用初始化列表

class A
{
public:
	int mA;
public:
	A()
	 {
		 mA = 0;
		cout << "A的无参构造" << endl;
		 }
	 A(int a)
		{
		 mA = a;
		 cout << "A的有参构造" << endl;
		 }
	~A()
	 {
		 cout << "A的析构函数" << endl;
	 }
};
class B
{
public:
	 int mB;
	A ob;//成员对象
public:
	B()
	{
		cout << "B类的无参构造" << endl;
	 }
		//初始化列表 成员对象 必须使用对象名+() 重要
	 B(int a, int b) :ob(a)
	{
		 mB = b;
		cout << "B类的有参构造" << endl;
	}
	~B()
	 {
		cout << "B的析构函数" << endl;
	 }
 };
int main(int argc, char* argv[])
{
	 B ob1(10, 20);
	 cout << "mA =" << ob1.ob.mA << ", mB =" << ob1.mB << endl;
	return 0;
 }

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第11张图片

 八、对象数组

        对象数组:本质是数组 数组的每个元素是对象

【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南_第12张图片

你可能感兴趣的:(c++,开发语言)