C++中类的封装

C++——类的封装

一、构造函数

1、利用构造函数完成对象的初始化

	C++中构造函数是专门用来初始化对象的成员函数。它是一种特殊的成员函数,它不需要用户来调用,而是
在建立对象的时候自动调用。

说明:
1、构造函数在建立对象时被自动调用
2、构造函数名与类名相同,无返回值,一般声明为public
3、用户如果没有定义构造函数,则系统会自动生成一个。但是函数体为空,无参数、不执行任何初始化操作

(1)在构造函数的函数体内进行赋值操作

函数首部一般为:
构造函数名(类型1  形参1, 类型2  形参2, ........)
{
	函数体内完成对象数据成员进行赋值操作;
}

定义对象时:
类名  对象名(实参1,实参2,.....)
using namespace std;
#include <iostream>

class Person{
public:
	Person(string name, char sex);
	void showPerson();
private:
	string name;
	char sex;
};
Person::Person(string name, char sex)
{
	//构造函数逇函数体内进行赋值操作
	this->name = name;
	this->sex = sex;
}
void Person::showPerson()
{
	cout << "name : " << name << endl;
	cout << "sex : " << sex << endl;
}
int main()
{
	Person p1("lilei", 'M');
	p1.showPerson();
	return 0;
}
运行结果:
name : lilei
sex : M

(2)用参数初始化列表完成对象的初始化

	有些情况下不能通过在构造函数的函数体内进行赋值操作,必须利用构造函数的参数初始化列表对对象进行初
始化。必须用构造函数的参数初始化列表进行初始化的情况有如下几种:
1、含有const成员变量时必须有构造函数的参数初始化列表完成初始化 
2、含有成员对象时必须在构造函数的参数初始化列表中指定用哪个构造函数完成对成员对象的初始化
3、在继承层次中,子类构造函数的参数初始化列表中必须指定调用父类的哪个构造函数完成对父类成员的初始化,
否则调用父类的无参构造函数对父类成员进行初始化。

(3)构造函数体内赋值和构造函数的参数初始化列表说明

1、从严格意义上来说,构造函数的执行分为两个阶段:首先执行构造函数的初始化列表,然后执行构造函数的函数
体赋值操作。
2、当使用构造函数的参数初始化列表对数据成员进行初始化时,类中所有的数据成员都会在执行构造函数的函数体
之前,通过构造函数的初始化列表完成初始化,无论该数据成员是否显式的出现在初始化列表中。
	
一般形式:
构造函数名(形参列表): 成员初始化列表
{
	函数体;
}
	
如果类的数据成员是数组,则应当在构造函数的函数体内对其赋值,而不能在参数初始化列表中完成。
例如:
char name[10] = "lieli", char name2[10] = {0};
name2 = name;//错误用法

在这里插入图片描述

例如:
class Person{
public:
	Person(char name[N], char sex, int age):sex(sex),age(age)
	{
		strcpy(name,name);
	}
private:
	char name[N];
	char sex;
	int age;
};

2、构造函数的重载

	构造函数可以重载,一个类可以有多个构造函数。在创建对象时会根据传入参数自动调用相应的构造函数。

3、使用默认参数的构造函数

	一个类只能有一个默认参数的构造函数,默认参数的构造函数即在定义对象时不需要传入实参的构造函数,
默认参数的构造函数有如下几种情况:
1)用户未定义构造函数时,由系统提供的无参构造函数
2)用户自定义的无参构造函数
3)有参构造函数的形参全部指定了默认值

如果定义对象时调用了无参构造函数,不需要给出实参。则应该注意定义对象时的格式:
Box box1;//定义对象,调用无参构造函数
Box box2();//错误,相当于定义一个函数;

4、拷贝构造函数

	拷贝构造函数是一种特殊的构造函数,一般不用自己定义,会由系统提供的默认的拷贝构造函数。拷贝构造
函数不可以被重载,它有固定的格式。
	默认的拷贝构造函数是按顺序对成员变量进行拷贝赋值的操作,也即浅拷贝;浅拷贝有些情况下并不适用,
因此特殊情况下需要自己定义拷贝构造函数,进行深拷贝。

拷贝构造函数的格式:
类名(const 类名& that)
{
	函数体;
}
	
拷贝构造函数的使用场景:
1)使用同类对象初始化另一个同类对象时
Person p2 = p1;  <==>  Person p2(p1);//都调用拷贝构造函数  
2)普通类对象作为函数形参时,实参与形参的虚实结合过程中调用拷贝构造函数
3)类对象作为函数返回值时,主调函数接收被调函数的返回值过程中调用拷贝构造函数
说明:因为调用拷贝构造函数需要开辟内存空间,比较消耗时间和空间,而一份相同的数据占用多个存储空间是
不合理的,因此通常用引用作为函数的形参和返回值,这样可以避免调用拷贝构造函数,提高效率。
using namespace std;
#include <iostream>

class Person{
public:
	Person(string name, char sex) : name(name), sex(sex){} //构造函数	
	Person(const Person& that){  //拷贝构造函数
		this->name = that.name;
		this->sex = that.sex;
	}
	void showPerson()
	{
		cout << "name : " << name << endl << "sex : " << sex <<endl;
	}
private:
	string name;
	char sex;
};
//全局函数:
void func(Person p)//实参与形参虚实结合过程中调用拷贝构造函数  
{
	cout << "func()" << endl;
}
void func2(const Person& p)//引用作为函数形参 
{
	cout << "func2()" << endl;
}

int main()
{
	Person p1("lilei", 'M');
	p1.showPerson();
	Person p2(p1); // <==> Person p2 = p1;用一个类对象初始化同类的对象时调用拷贝构造函数
	p2.showPerson();
	Person p3;
	p3 = p1;//这里调用的是赋值运算符函数(运算符的重载),并未调用拷贝构造函数。
	func(p1);//调用拷贝构造函数
	func2(p1);//未调用拷贝构造函数
	return 0;
}
	拷贝构造函数利用初始化列表的形式,向这种情况如果有const成员变量也需要重新定义拷贝构造函数。

二、析构函数

	析构函数用于销毁对象前执行一些清理工作,释放资源等。析构函数与构造函数的作用相反,当对象的生
存期结束时,会自动调用析构函数。
	
析构函数与构造函数的调用顺序:
	一般而言先构造的后析构,但析构的先后顺序不仅仅取决于构造的顺序,还取决于对象的存储类别,生命期。
静态存储区、动态存储区(堆区、栈区)。

总结:
1)先构造的后析构
2)根据存储类型及生存期来说,非静态局部变量、静态局部变量和全局变量、malloc或new获取的变量

三、无名对象与explict关键字

1、无名对象

	直接调用构造函数创建的对象是无名对象,无名对象的声明周期是临时的,又叫做临时对象。它的声明周
期在该语句所在的行。

假设Person是已声明的类:
Person p1("Tom", 'M');     //类类型  变量名 =》定义了一个有名对象      
Person("Tom, 'M');     //直接调用构造函数 =》 定义了一个无名对象  

无名对象的用法:
Person p2;                              优于:----》 Person p1("Tom", 'M');
p2 = Person("Tom", 'M');                             p2 = p1;

例题:用一个无名对象去初始化一个同类的对象

在这里插入图片描述

2、explict关键字

	explicit关键字可以用来修饰单参构造函数,禁止隐式的构造单参无名对象。在C++中常用explicit修饰
一个参数的构造函数(或者除了第一个参数外,其余参数都有默认值的多参构造函数),这时会禁止构造函数
进行隐式转换;否则explicit关键字不起作用。
using namespace std;
#include <iostream>

class Person{
public:
	explicit Person(string name) : name(name){}
	void showPerson(){
		cout << "name : " << name << endl;
	}
private:
	string name;
};
int main()
{
	//Person p1 = string("lilei");//隐式构造,当单参构造函数没有用explicit做声明时成立
	//p1.showPerson();
	Person p2 = Person(string("lilei");//显示构造
	p2.showPerson();
	return 0;
}

四、const成员与对象

1、const成员

1、const数据成员:
	const数据成员和普通的const成员变量类似,这里不做赘述。

2、const成员函数
一般形式: 返回值类型  函数名(形参列表) const;

	通常将不打算修改成员变量的函数声明为const,不可以用于修饰普通函数;const修饰的成员函数不可
直接或者间接的修改成员变量,因此规定const成员函数不可以调用非const成员函数;同名同参的成员函数,
const与非const构成重载关系。

2、const对象

	const对象只能调用const成员函数,不可以调用非const成员函数;非const对象会优先选择非const版本
的成员函数。

五、static静态成员

1、static静态成员变量

	static静态成员变量用来描述本类中所有对象的某一共有属性。本类的所有对象的static数据成员都是同
一块内存,存放在静态存储区。

注意:
	static成员变量在类内进行声明,在类外进行定义和初始化。变量的定义需要开辟内存,而类的定义是抽象的,
没有进行实例化并不会开辟内存空间。因此,只可在类内对static成员进行声明,在类外进行定义和初始化。

声明:static  类型  静态成员变量名
定义:类型  类名::静态成员变量 = 初值
class Person{
private:
	static int counter;//静态成员变量的声明
};
int Person::counter = 0;//类外进行静态成员变量的定义和初始化	
注意:
	在建立对象时不可以通过初始化列表对静态成员变量进行初始化(变量只能初始化一次,static成员变量是该
类的公有属性,如果每个对象都在初始化列表中对静态成员变量进行了初始化则会矛盾),如果想改变静态成员变
量的值,可以在构造函数的函数体内实现。静态成员变量如果为private时,不可以在类外进行访问,仍要通过类
的成员函数才可以访问。

2、静态成员函数

一般形式: static  返回值类型  函数名(形参列表)
静态成员函数用于访问静态成员变量;静态成员函数可以通过对象名调用或者类名进行调用。

静态成员函数与非静态成员函数的区别:
非静态成员函数有this指针,而静态成员函数没有。因此静态成员函数无法访问非静态成员变量。
using namespace std;
#include <iostream>

class Student{
public:
	static void showCounter(){
		cout << "counter : " << counter << endl;
	}//静态成员函数用于访问静态数据成员,而不可以访问非静态数据成员
private:
	static int counter;//静态成员变量在类内进行声明 
};
int Student::counter = 0;//静态成员变量在类外进行定义和初始化

int main()
{
	Student stu1;
	stu1.showCounter();//通过类对象调用静态成员函数
	Student::showCounter();//通过类名和域名限定符::来调用静态成员函数
	return 0;
}

六、this指针

1、概念:
(1)类的非静态成员函数都有一个隐藏的形参名为this,类型是 " T*",类指针
(2)this指针的作用域是非静态成员函数的函数体
(3)this指针保存的是当前对象的地址
(4)对于构造函数,this指针指向正在被创建的对象。

2、this指针的引用:
(1)避免名字冲突:
Person (string name, char sex, int age)
{	
	this->name = name;
	this->sex = sex;
	this->age =age;
}

(2)返回调用对象本身的地址:
类名*  func()                               类名&  func()
{                                           {
	return this;                    	       	return *this;
}											}

七、成员对象的构造与析构

1)成员对象的初始化由成员对象的构造函数负责
2)成员对象的构造在当前对象的构造之前,成员对象的析构在当前对象之后。
3)多个成员对象的构造顺序与成员对象在类中定义的先后顺序相关,与成员对象在初始化列表中的顺序无关。

含有成员对象的类的构造函数一般形式:
类的构造函数名(总形参数表):成员对象名(实参表)
{
	其它数据成员的初始化;
}

说明:
1)对成员对象的初始化只能在初始化列表中完成。
2)含有成员对象的类对象的一次初始化过程需要两次调用成员对象的构造函数或者拷贝构造函数。
using namespace std;
#include <iostrema>

class People{
public:
	//People的构造函数
	People(string name, char sex):name(name),sex(sex)
	{
		cout << "People的构造函数" << endl;
	}
	//People的拷贝构造函数
	People(const People& that)
	{
		this->name = that.name;
		this->sex = that.sex;
		cout << "People的拷贝构造函数" << endl;
	}
private:
	string name;
	char sex;
};

class Student{
public:
	//以下是Student类的构造函数的两种不同形式
	Student(int number, People p1):number(number),p1(p1)
	{
		cout << "Student(int number, People p1)" << endl;
	}//至少一次调用成员对象的拷贝构造函数 
	Student(int nunber, string name, char sex):number(number),p1(name, sex)
	{
		cout << "Student(int number, string name, char sex)" << endl;
	}//调用一次成员对象的构造函数
private:
	int number;
	People p1;//成员对象
};

int main()
{
	People p("wanghao",'M');
	Student stud1(1001, p);//调用两次成员对象Person的拷贝构造函数;1)对象p去初始化Student的
	//构造函数总参数列表;2)Student构造函数总参数列表中的p1区初始化成员对象p1
	cout << "==================================" << endl;

	//说明:
	//People p1 = People("lilei", 'M');
	//使用无名对象去初始化另一个同名的对象只会调用一次People类的含参构造函数,并不会调用People
	//的拷贝构造函数
	
	Student stud2(1001, People("wanghao",'M'));//成员对象的构造函数和拷贝构造函数分别调用一次
	//1)无名对象People("wanghao", 'M')去初始化Student总参数列表中的p1,调用一次成员对象的构造
	//函数;2)Student构造函数总参数列表中的p1去初始化成员对象p1,调用成员对象的拷贝构造函数
	cout << "===================================" << endl;
	
	Student stud3(1001,string("wanghaoo"),'M');//只调用一次成员对象的构造函数;在Student的
	//构造函数中用总形参列表中的name和char去初始化成员对象p1
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C++中的类是面向对象编程的基本单元,其中封装是其最重要的特征之一。封装可确保数据和函数都能被保护起来,不会被外部访问和修改,而只能通过类定义的公共接口来操作这些数据。 Dll是动态链接库(Dynamic Link Library)的缩写,它是一种库文件,可以在编译时链接到可执行文件,也可以在运行时加载。封装在动态链接库中使用可以更好地保障其安全性和灵活性。 在C++中,类的封装可以被编译成动态链接库,也称为类库(Class Library)。这种方式可以使开发者将代码分成多个库文件,并且仅将需要的库文件链接到可执行文件中。这种方式可以在多个程序之间共享类定义和函数实现,减少代码重复并提高代码的可维护性。 同时,如果只需修改类库中的某个类的实现,而其他类的实现没有改变,那么只需重新编译该库就可以了,而不用重新编译所有代码。 在使用动态链接库时,需要使用C++的导出和导入关键字将类和函数导出到DLL中,并在可执行程序中使用它们。这些关键字包括__declspec(dllexport)和__declspec(dllimport)等。 总之,在C++中使用类的封装和dll可以使代码变得更加安全,可维护性更高,并有效地减少代码重复。 ### 回答2: C语言中,类的封装需要使用结构体和函数来实现,其中结构体表示对象的属性,而函数则表示对象的方法。为了更好地维护代码,实现代码的复用,我们可以将类的封装通过dll动态链接库的方式实现。 首先,我们需要定义一个包含结构体和函数声明的header文件,用来描述类的结构和功能,然后将这个header文件编译成动态链接库,供其他程序调用。这样做的好处在于,只要我们在其他程序中引用了这个dll,就可以直接使用这个类,无需重新编写代码,提高了代码的复用性。 同时,在dll封装中,我们也需要注意一些问题,比如动态链接库的版本问题,如果不同的程序引用了不同版本的dll,可能会出现运行错误,所以需要在编写dll时考虑这些问题。在使用dll时,也需要注意动态链接库的加载和释放问题,防止出现内存泄漏等问题。 总之,通过dll的封装,可以更加方便地实现C语言中的类的封装,提高代码的复用性和可维护性,但需要在实现过程中注意一些问题,提高代码的质量。 ### 回答3: C++类的封装 DLL是一种将类库打包为可重用代码的方式。DLL全称为Dynamic Link Library,动态链接库。它是一种Windows系统下的动态链接库文件,提供了一种动态加载可以在运行时执行的代码和数据的方式。使用DLL的好处在于,它可以节省资源并提高程序的灵活性。 C++类的封装 DLL可以将类封装为可直接调用的动态链接库。这样,其他程序就可以直接使用这个类库,而无需自行编写相应的代码。使用DLL可以使程序员们更加专注于自己的工作而不必担心代码的实现细节。 封装DLL还有另外一些优点,例如:DLL文件可以被多个应用程序所共享,因此可以减少内存的消耗,提高程序的运行效率。同时,类库也可以在项目开发过程中进行独立开发和单元测试,这样可以更好地提高程序的质量和可维护性。 总之,C++类的封装 DLL是一种非常有用的开发技术,它能够有效地提高程序的复用性、可移植性和可维护性。因此,对于需要维护大型项目的开发团队来说,封装DLL是一种非常具有实际意义的开发策略。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值