C++ 4:面向对象编程,this指针,初始化对象,构造函数和析构函数,对象的分类

本文详细探讨了模板函数、类与对象的结构、面向对象特性、封装与继承、成员函数与this指针,重点讲解了构造函数、析构函数、对象生存期以及动态创建对象的过程。通过实例演示,理解了如何设计和实例化类型,以及如何正确使用构造与析构函数来管理对象生命周期。
摘要由CSDN通过智能技术生成

1. 模板函数

(1)模板函数在编译的时候进行实例化,实例化时候进行模板推演规则

void Print_Ar(Type(&br)[N])
{
    for (int i = 0; i < N; ++i)
    {
        cout << br[i] << " ";
    }
    cout << endl;
}

(2)编译时候系统内部变化

处理整型变量

typedef int Type;
void Print_Ar<int,7>(Type(&br)[7])
{
    for (int i = 0; i < 7; ++i)
    {
        cout << br[i] << " ";
    }
    cout << endl;
}

非类型可以替换成宏,类型不能替换为宏,类型是一种重命名规则,非类型可以为替换规则。

2. 面向对象编程

2.1 面向对象概述

2.1.1 面向对象概念

  • 1.模拟现实世界,用面向对象世界模拟现实世界,用编程语言来面向对象

  • 2.面向对象:对象的概念是面向对象技术的核心所在。面向对象技术中的对象就是现实世界中某个具体的物理实体在计算机逻辑中的映射和体现。
    现实世界和计算机世界映射

2.1.2 类、对象、成员变量、成员函数

  • 类是一组相关的属性(变量)和行为(方法)的集合,是一个抽象概念设计的产物。
  • 对象是该类事物的具体表现形式,具体存在的实体。
  • 成员变量是对象的属性(可以是量,指针,数组等),属性的值确定对象的状态。
  • 成员函数是对象的方法,确定对象的行为。

2.2 状态和行为

状态和行为是对象的主要属性

  • 对象的状态又称为对象的静态属性,主要指对象内部所包含的各种信息,也就是变量。每个对象个体都有自己专有的内部变量,这些变量的值标明了对象所处的状态。
  • 对象的方法(行为)一方面把对象的内部变量包裹,封装,保护起来,使得只有对象自己的方法才能操作这些内部变量,另一方面,对象的方法还是对象与外部环境和其他对象交互,通信的接口,对象的环境和其他对象可以通过这个接口来调用对象的方法,操纵对象的行为和改变对象的状态。

对象是现实世界的实体或概念在计算机世界中的抽象表示。具体地,对象是属性和操作的一个集合,用来模拟或影响现实世界中的实体。

2.3 面向对象特点

  • 封装:把数据(属性)和函数(操作)合成一个整体
  • 关键字class是数据类型说明符,指出下面说明的类。标识符是类的类型名。访问限定符有三种:public,private和protected。Public只能从外部进行访问,每种说明符在类体中可多次使用,他们的作用域是从该说明符出现到下一个说明符之前结束。

2.4 对象的关系

  • 包含:当对象A是对象B的属性时,称对象B包含对象A.
  • 继承:当对象A是对象B的特例时,称对象A继承对象B.
  • 关联:当对象A的引用是对象B的属性时,称对象A和对象B之间是关联关系.所谓对象的引用是指对象的名称,地址,句柄等可以获得和操纵该对象的途径。

2.5 成员数据

class CGoods
{
public:
	char Name[LEN];
	int AMount;
	float Price;
	float Total_value;
};

访问限定符(access specifier)有三种: public (公共的), private (私有的)和protected(保护的)。第一种说明的成员能从外部进行访问,另外两种说明的成员不能从外部进行访问。每种说明符可在类体中使用多次。它们的作用域是从该说明符出现开始到下一个说明符之前或类体结束之前结束。

2.6 struct和class设计类型区别?

struct设计类型默认属性是公有类型,class设计类型默认属性的私有类型

2.7 成员函数

函数/方法标识类型具有行为,属性标识类型具有状态

类型设计的更关键部分是对数据成员的操作,用函数来完成:

class CGoods
{
private:
	char Name[LEN];
	int Amount;
	float Price;
	float Total_value;
public:
	void RegisterGoods(char[], int, float);//输入数据
	void CountTotal(void);//计算商品总价值
	void GetName(char[]);//读取商品名
	int GetAmount(void);//读取商品数量
	float GetPrice(void);//读取商品单价
	float GetTotal_value(void);//读取商品总价值
};
  • 这样在类中引进了成员函数(member function)或函数成员,也就是函数也成了数据(类)中的一员。类把数据(事物的属性)和函数(事物的行为–操作)封装为一个整体。

  • 四个数据成员被说明成私有,而六个函数成员被说明成公有;这就是说如果从外部对四个数据成员进行操作的话只能通过六个公有函数来完成,数据受到了良好的保护,不易受外部环境的影响。公有函数集定义了类的接口(interface) 。

  • 类是一种数据类型,定义时系统不为类分配存储空间,所以不能对类的数据成员初始化。类中的任
    何数据成员也不能使用关键字extern、auto或register限定其存储类型。

2.8 成员函数定义声明

2.8.1 类内声明,类外定义

在这里插入图片描述

2.8.2 类内声明,类内定义

在这里插入图片描述
区别:在类内声明且定义的成员函数会被系统默认为内联函数,在类外定义的函数必须加上inline关键字才被认为是内联函数

3. 类型设计与实例化

3.1 面向对象特性

  • 封装性
  • 继承性
  • 多态性

3.2 封装性

3.2.1 封装的概念

封装(Encapsulation)是面向对象程序设计最基本的特性,把数据(属性)和函数(操作)合成一个整体,这在计算机世界中是用类与对象实现的。

class 类型名称
{
public:
	成员列表1;
protected:
	成员列表2;
private:
	成员列表3};

如果在类体起始点无访问说明符,系统默认定义为私有( private )。访问说明符private (私有的)和protected(保护的)体现了类具有封装性(Encapsulation)

3.3 对象的创建与使用

对象是类的实例(instance) 。 声明一种数据类型只是告诉编译系统该数据类型的构造,并没有预定内存。类只是一个样板 (图纸), 以此样板可以在内存中开辟出同样结构的实例——对象。

3.4 C++对象模型讨论

3.4.1 各对象完全独立地安排内存

在这里插入图片描述

3.4.2 各对象的代码区共用

在这里插入图片描述

4.this指针

4.1 this指针作用

编译器针对程序员自己设计的类型分三次编译。

  • 第一:识别和记录类体中属性的名称,类型和访问限定,与属性在类体中的位置无关。如class CGoods中的Name, Amount,Price, Total_value;
  • 第二:识别和记录类体中函数原型(返回类型+函数名+参数列表),形参的默认值,访问限定。不识别函数体。
  • 第三:改写在类中定义函数的参数列表和函数体,改写对象调用成员函数的形式;
    在这里插入图片描述
    凡是在函数体中出现的标识符名字和类体内属性名称相同时,加入this指针,如果程序员没有加入,系统会自己给出
    在这里插入图片描述
    this指针是函数的一个形参,函数被调用,this指针产生,函数调用结束,this指针消失出
int *const this

this指针为常性

5. 构造函数和析构函数

5.1 构造函数

5.1.1 构造函数概念

数据成员多为私有的,要对它们进行初始化,必须用一个共有函数来进行。同时这个函数应该在且在定义对象时自动执行一次,称为构造函数。

5.1.2 构造函数用途

  • ① 创建对象
  • ② 初始化对象中的属性
  • ③类型转换

5.1.3 构造函数的定义与使用

构造函数是特殊的公有成员函数(在特殊用途中构造函数的访问限定可以定义成私有或保护) ,其特征如下:

  • ① 函数名与类名相同。
  • ② 构造函数无函数返回类型说明。注意是没有而不是void,即什么也不写,也不可写void。实际上构造函数有返回值,返回的就是构造函数所创建的对象。
  • ③ 在程序运行时,当新的对象被建立,该对象所属的类构造函数自动被调用,在该对象生存期中也只调用这一次。(系统内调动,调动一次)
  • ④ 构造函数可以重载。严格地讲,类中可以定义多个构造函数,它们由不同的参数表区分系统在自动调用时按一般函数重载的规则选一个执行。
  • ⑤ 构造函数可以在类中定义,也可以在类中声明,在类外定义。
  • ⑥ 如果类说明中没有给出构造函数,则C++编译器自动给出一个缺省的构造函数:
    类名(void){ }

但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数。只要构造函数是无参的或者只要各参数均有缺省值的,C++编译器都认为是缺省的构造函数,并且缺省的构造函数只能有一个,对象不能调动构造函数。

空间:系统分配产生,有空间不一定有对象,有对象一定有空间,空间是由于调用构造函数构建对象
在这里插入图片描述
析构函数由系统调动,而且只能调动一次

用定位new可以实现构造函数多次调动

int main()
{
	Complex c1;
	Complex c2(12, 23);
	new(&c1) Complex(23, 34);//用定位new可以实现构造函数多次调动
	c1.Print();

	return 0;
}

5.1.4 对象初始化

系统调动的缺省构造函数只能对对象进行构造,不能初始化对象
在这里插入图片描述

5.1.5 赋值和初始化列表对对象进行初始化区别

在这里插入图片描述
在这里插入图片描述
初始化列表初始化对象调动构造函数,创建对象进行初始化,效率比较高

5.1.6 缺省构造函数和普通构造函数

#include<iostream>
#include<string.h>
using namespace std;

class CGoods
{
private:
	enum { LEN = 20 };
private:
	char Name[LEN];
	int Amount;
	float Price;
	float Total;
public:
	//CGoods() {}
	CGoods(const char* name = nullptr, int amount = 0, float price = 0)
		:Amount(amount), Price(price), Total(amount* price)
	{
		if (name != NULL)
		{
			strcpy_s(Name, LEN, name);
		}
		else
		{
			Name[0] = '\0';//输入空字符串,不合法
		}
	}
	void GoodsInfo() const
	{
		cout << "Name:" << Name << endl;
		cout << "Amount:" << Amount << endl;
		cout << "Price:" << Price << endl;
		cout << "Total:" << Total << endl;
	}
};
int main()
{
	CGoods c1;
	//cout << sizeof(c1) << endl;//32字节
	CGoods c2("BYD", 10, 12800.00);
	CGoods c3("C++", 10, 128.00);
	c2.GoodsInfo();
	c3.GoodsInfo();
}
5.1.6.1 关于strcpy_s问题
#include<iostream>
#include<string.h>
using namespace std;

char Name[20];
const char* name = "hexiaoqi";
strcpy_s(Name, strlen(name) + 1, name);

在这里插入图片描述

5.1.7 构造函数之类型转换

#include<iostream>
#include<string>
using namespace std;

class Int
{
private:
	int value;
public:
	Int(int x=0) :value(x) {}
	void Print() const
	{
		cout << value << endl;
	}
};
int main()
{
	Int a(10);
	int x = 100;
	a.Print();
	a = x;//不同类型进行赋值
	a.Print();

	return 0;
}

5.1.8 右值

左值:具有名字,可以去地址
右值:不具有名字,不能取地址
将亡值:不具名对象
纯右值:字面常量
显式转换:a = (Int)x;//显式转换
隐式转换:a = x;//不同类型进行赋值(隐式转换)
在这里插入图片描述
逗号表达式特殊用法
在这里插入图片描述

5.2 析构函数

5.2.1 析构函数定义

当定义一个对象时,C++ 自动调用构造函数建立该对象并进行初始化,那么当一个对象的生命周期结束时,C++也会自动调用一个函数注销该对象并进行善后工作,这个特殊的成员函数即析构函数(destructor) :

  • (1)析构函数名与类名相同,但在前面加上字符~。如: ~CGoods ()。
  • (2)析构函数无函数返回类型,与构造函数在这方面是一样的。但析构函数不带任何参数。
  • (3)一个类有一个也只有一个析构函数,这与构造函数不同。
  • (4)对象注销时,系统自动调用析构函数。
  • (5)如果类说明中没有给出析构函数,则C+ +编译器自动给出一个缺省的析构函数。如: ~类名 (){}
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>

using namespace std;

class Pointer
{
private:
	int row;
	int col;
public:
	Pointer(int r,int c)
	{
		row=r;
		col=c;
	}
	~Pointer()
	{
		cout<<"~Pointer"<<endl;
	}
};

Pointer g_pt(12,23);//全局对象.data;
int main()
{
	Pointer p1(100,200);//局部对象.stack;
	Pointer* p=NULL;
	p=new Pointer(10,20);//new(malloc从堆区申请空间,构建对象,把地址空间返回给p)
	Pointer* s=(Pointer*)malloc(sizeof(Pointer));
	
}
/*int main()
{
	Pointer c1(12,23);
	Pointer c2;

	c1.~Pointer();
	return 0;
}*/
class Object
{
private:
	int val;
public:
	Object(int x)
	{
		val=x;
		cout<<"Create"<<val<<endl;
	}
};
Object ObjA(1);
int main()
{
	Object ObjB(2);
}
Object ObjC(3);

6.对象的生存期

6.1 对象的分类

  • 局部对象(栈区)
  • 全局对象(数据区)
  • 动态创建的对象(堆区)
    构造对象分为静态构造对象和动态构造对象
#include<iostream>
using namespace std;

class Object
{
    int value;
public:
    Object(int x=0):value(x){}
    void Print()const { cout << value << endl; }
};

Object g_obj(10);//全局变量 .data
//当进入主函数之前创建全局对象,全局对象创建和主函数位置无关系,整个程序结束才能销毁(程序生存期)
void fun()
{
    Object obja(20);//局部变量 .stack
    //当函数被调用,对象调动构造函数,在fun栈帧中创建对象,当函数结束,对象生存期结束,对象调动析构函数(函数生存期)
    obja.Print();
}
int main()
{
    fun();
    Object* p = new Object(20);//对象.heap
    //申请空间,在空间中创造对象,把创建好的对象的地址返回,使用完指针要进行释放
}
int main()
{
    int n;
    Object* s = new Object(n);//构建一个对象
    Object* p = new Object[n];//构建一组对象

    //构建对象方式不同,析构也不同
    delete s;
    delete[]p;

    return 0;
}

析构对象顺序:先构造的对象后析构,后构造的对象先析构

6.2 为什么反复创建的临时对象地址总是相同的?

在这里插入图片描述

6.3 静态对象

静态对象是全局对象,生存期是整个程序结束之前,只有在整个程序结束之后静态变量才会析构掉。

6.4 全局对象

在这里插入图片描述

7. 动态创建对象

malloc函数
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值