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

目录

一、类的引出概述

二、封装

三、类的案例(了解)

1、设计一个person类

2、设计一个立方体类

3、点和圆的关系

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

5、类在其他文件实现

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

1、概述

2、创建

3、分类

4、调用

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

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

六、拷贝构造函数

1、定义

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

3、拷贝构造调用形式

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

 七、初始化列表

 八、对象数组


 

一、类的引出概述

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

二、封装

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

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

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

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

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

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

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

优点:

对变量的设置时的控制

实现变量设置只读权限

实现变量设置只写权限

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

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

三、类的案例(了解)

1、设计一个person类

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

#include <iostream>
using namespace std;
#include<string.h>
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<<"年龄无效"<<endl;
        }
        return;
    }
    //设置name
    void setName(char *name)
    {
        strcpy(mName, name);
    }
     //获取name
    char *getName(void)
    {
         return mName;
     }
    //设置age
    void setAge(int age)
    {
        if(age>=0 && age<=100)
        {
            mAge = age;
        }
        else
        {
            cout<<"年龄无效"<<endl;
        }
    }
    //得到age
    int getAge(void)
    {
        return mAge;
    }
    //显示所有数据
    void showPerson(void)
    {
        cout<<mName<<" "<<mAge<<endl;
    }
};
void test02()
{
    Person ob1;
    ob1.init("lucy", 18);
    ob1.showPerson();
    ob1.setName("bob");
    cout<<"年龄:"<<ob1.getAge()<<endl;
    ob1.showPerson();
}

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<<"面积:"<<ob1.getS()<<endl;
    cout<<"体积:"<<ob1.getV()<<endl;
    Cube ob2;
    ob2.setA(10);
    ob2.setB(20);
    ob2.setC(30);
    // if(compareCube02(ob1, ob2))
    if(ob1.compareCube01(ob2))
    {
        cout<<"相等"<<endl;
    }
    else
    {
        cout<<"不相等"<<endl;
    }
}
int main(int argc, char *argv[])
{
    test();
    return 0;
}

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<<"点在圆上"<<endl;
    }
    else if(cir.pointIsOnCircle(p) > 0)
    {
        cout<<"点在圆外"<<endl;
    }
    else if(cir.pointIsOnCircle(p) < 0)
    {
        cout<<"点在圆内"<<endl;
    }
}

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

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 <iostream>
#include "data.h"
using namespace std;
int main(int argc, char *argv[])
{
    Data ob;
    ob.setA(100);
    cout<<ob.getA()<<endl;
    return 0;
}

对象的构造和析构:

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

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

1、概述

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

2、创建

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

3、分类

        无参构造、有参构造。

4、调用

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

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

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

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

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

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

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

允许隐式转换:

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

不允许隐式转换:

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

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

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

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

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

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

#include<iostream>
#include<stdlib.h>
#include<string.h>
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 << "析构函数" <<name<< endl;
		if (name != NULL)
		{
			free(name);
			name = NULL;
		}
	}
 };
void test()
{	
	Data ob("hello world");
}

六、拷贝构造函数

1、定义

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

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

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

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

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

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

3、拷贝构造调用形式

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

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

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

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

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

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

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

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

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

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

 七、初始化列表

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

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

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;
 }

 八、对象数组

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

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值