C++初阶:类和对象(一)——类的创建与使用

本文介绍了C++中的面向对象编程概念,包括类的引入、定义、访问权限、作用域、实例化、类对象模型,重点讲解了this指针的作用。同时涉及了结构体和内存对齐等基础知识。
摘要由CSDN通过智能技术生成

        C语言是一门面向过程的语言,C++是一门面向对象的语言,本节将会初步讲述面向对象的编程思想以及类的创建。

编者注:从本讲开始在代码模块不会全部包含头文件,一些常见的头文件请读者在cv大法后自行添加。

目录

一、面向过程与面向对象的初步认识

二、类的引入

三、类的定义

1.基本了解

2.定义方式

四、类的访问

五、类的作用域

六、类的实例化

七、类对象模型

1.计算类对象的大小

2.结构体的内存对齐

八、this指针

1.引子

 2.this指针的特性


一、面向过程与面向对象的初步认识

        

        大象塞冰箱是C语言中最常见的模型之一,步骤可以简化为上图所示。该操作能实现将大象塞进冰箱的操作,关注的是过程,分析求解问题的过程,通过调用不同的函数来逐步地解决问题。而C++是一门面向对象的语言,基于C++的考虑方式,大象进冰箱又可以分装为两个对象,每一个对象里面包含了自己的属性和一些方法,如下图:

        C++是基于面向对象的,关注的是对象,将一件事情拆分成为不同的对象,由不同对象之间的交互来完成这件事。

二、类的引入

        在C语言中,我们能通过结构体来包装一系列的变量,在C++中结构体的功能得到了一定的强化,在结构体中不仅可以定义变量,还能定义函数。如下面一串代码:

struct Student
{
	int num;
	char name[20];

	void showMessage()
	{
		cout << "num:" << num << endl;
		cout << "name:" << name << endl;
	}
};
int main()
{
    //在C++中定义了一个结构体之后,使用其创建变量就不需要再加struct了,也不需要用typedef来重命名
	Student a = { 18,"xiaoming" };
    //在调用结构体内部的函数时,方法同调用普通变量一样即可
	a.showMessage();
	return 0;
}

        但是在C++中,引入了class类的概念,我们更喜欢用class来进行操作。

三、类的定义

class Name
{
    //类体:由成员变量和成员函数组成
};//分号是不可以省略的

1.基本了解

         class是定义一个类的关键字,Name为类的名字,你可以对其任意命名,但是最好是具有一定的实意,{}中是类的主体,类定义完后的分号不可以省略。

        类体中的内容称为类的成员:其中的变量称为成员变量或者类的属性;其中的函数称为成员函数或者是类的方法。

2.定义方式

(1)声明与定义全部放在内体中,但是需要注意的是:成员函数如果在类中定义,编译器可能会将其处理为内联(inline)函数,例如:

class Student
{
	int num;
	char name[20];

	void showMessage()
	{
		cout << "num:" << num << endl;
		cout << "name:" << name << endl;
	}
};

(2)类声明之后,类中函数在类外定义,此时需要加上::域限定作用符,以表示其是该类中的函数,例如:

class Student
{
	int num;
	char name[20];

	void showMessage();
};

void Student::showMessage()
{
	cout << "num:" << num << endl;
	cout << "name:" << name << endl;
}
 

        在一般的情况下,我们更推荐采用第二种做法,便于适应日后工程化编程的需求。

四、类的访问

        在上面的代码中,如果你想定义一个Student类的变量,并给它赋初值,在VS的环境下,你会发现还没有编译,而编译器就已经给你报红了。这是为什么呢?因为在class类中,成员默认是私有的,不能被外界访问,如果你需要访问它当中的成员,则需要使用类的访问限定符对其进行封装。

int main()
{
    //error
	Student a = { 18,"xiaoming" };
	a.showMessage();
	return 0;
}

         类的访问限定符有三个:public(公有)、protected(保护)、private(私有)

  1. public覆盖的成员允许被外界访问
  2. protected和private覆盖的成员在类外不能被直接访问(在目前阶段,二者类似)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止
  4. 如果没有再出现访问限定符,作用域到 } 就结束
  5. class的默认访问操作权限是private,而struct的默认访问权限是public(因为C++兼容了C),这也就说明在struct中也可以使用访问限定符来设置访问的权限,操作与class的类似。

        面向对象的三大特性:封装、继承、多态。

        目前,在类和对象的阶段主要研究的是封装特性。封装是什么?封装是将数据和操作数据的方法进行有机的结合,隐藏对象的属性和实现细节,仅对外公开的接口和对象进行交互。本质是一种管理,让用户更容易使用类。

五、类的作用域

        类定义了一个全新的作用域,类的所有成员都在类中,在类外定义类中的成员时,需要使用::作用域操作符来指明成员属于哪个域——这一点与namespace命名空间是类似的。

class Student
{
private:
	int num;
	char name[20];
public:
	void showMessage();
	void Init();
};
void Student::showMessage()
{
	cout << "num:" << num << endl;
	cout << "name:" << name << endl;
}
void Student::Init()
{
	num = 1;
	strcpy(name,"xiaoming");
}
int main()
{
	Student a;
	a.Init();
	a.showMessage();
	return 0;
}

六、类的实例化

        在C语言中,我们使用结构体创建变量,在C++中,我们使用类来创建变量,该情况下的变量我们习惯于称为“对象”。用类来创建对象的过程,被我们称为类的实例化。

  1. 类是对对象进行描述的,是一个模型一般的东西,限定了类中有哪些成员,定义处一个类都是并没有被分配实际的内存空间来存储。
  2. 一个类作为一个模版,可以实例化多个对象,实例化出来的对象占据实际的物理空间用来存储类中的成员变量(也只是成员变量哦,后续会提到)。
  3. 同一个类创建的对象具有相同类型的成员变量,访问时只需要对象名.属性名就可以了。
int main()
{
	Student.num=3;//error 语法错误 因为Student是一个类,本身没有空间
	
	return 0;
}

七、类对象模型

1.计算类对象的大小

class Student
{
private:
	int num;
	char name[20];
public:
	void showMessage();
	void Init();
};
void Student::showMessage()
{
	cout << "num:" << num << endl;
	cout << "name:" << name << endl;
}
void Student::Init()
{
	num = 1;
	strcpy(name,"xiaoming");
}
int main()
{
	Student a;
	cout<< sizeof(a) << endl;
	return 0;
}

        请猜测控制台的打印结果。

        运行上面的一串代码 ,可以看到控制台打印了一个24,可是我不是在类中定义了函数的吗?函数应该是占有内存的啊,在x86的环境下函数指针的大小是4个字节,在x64的环境下是8个字节,这内存空间大小不对啊!

        现在请思考一个问题:如果我利用该类创建了多个对象,然后都调用了函数,可是函数的功能却都是相同的,在不同的对象中都有这个函数是否会造成内存的浪费?

        所以,在类中的成员函数是没有算在对象中的内存里面的。它们存储在一个公共的代码段中,可是我在调用时并没有传入指针或者引用那些东西啊,这是如何对我对象中的数据进行的操作呢?别急,稍后揭晓~我们先来看三道小题题:
 

class A1
{
public:
	void f1()
	{

	}
private:
	int a1;
};

class A2
{
public:
	void f2()
	{

	}
};

class A3
{

};

        请思考这串代码中sizeof(A1)、sizeof(A2)、sizeof(A3)的大小……

        答案是:4 1 1

 结论:一个类的大小,实际就是该类中成员变量的内存大小之和,当然要注意内存对齐。如果一个类不包含成员变量,编译器则会给其分配一个字节来进行标识。

2.结构体的内存对齐

  1. 第一个成员在与结构体变量偏移量为0的地址处
  2. 从第二个成员开始,以后每个成员都要对齐到某个对齐数的整数倍位置
  3. 这个对齐数是自身成员大小和默认成员对齐数的较小值 备注:vs环境下默认成员对齐数是8
  4. 当成员全部放入后,结构体的总大小必须是所有成员对齐数中最大对齐数的整数倍
  5. 如果嵌套了结构体,嵌套的结构体成员要对齐到自己成员的最大对齐数的整数倍处。整个结构体的大小,必须是最大对齐数的整数倍,最大对齐数包含嵌套结构体的对齐数。

八、this指针

1.引子

在“七.1”中留的悬念就是这个this指针了,this指针是类可以复用性好的基础。

        C++编译器给每一个非静态的成员函数都添加了一个隐形的指针参数,让该指针指向当前的对象(函数进行调用时调用该函数的对象),在函数体中所有的“成员变量”的操作其实都是通过this指针去操作的。只不过该this指针对于用户的操作来说是透明不可见的,即用户不需要传递对象的地址,也不需要在函数中添加该类的指针(这会导致报错),编译器来帮你完成。

 2.this指针的特性

  • this指针的类型:类名*const,即在成员函数中,无法给this指针赋值
  • this指针只能在成员函数的内部使用
  • this指针本质上是“成员函数”的形参,当对象调用了成员函数时,编译器会将对象的地址传递给this形参。因此对象中并没有存储this指针
  • this指针是“成员函数”第一个隐含的参数,一般有编译器通过ecx寄存器自动传递,不需要用户进行手动传递
//可以这样书写
void Student::showMessage()
{
	cout << "num:" << num << endl;
	cout << "name:" << name << endl;
}
//不能这样书写,请注意下面的函数参数中const Student*this不能存在,编译器会报错
void Student::showMessage(Student const* this)
{
	cout << "num:" << this->num << endl;
	cout << "name:" << this->name << endl;
}
//修改之后才可以哦
void Student::showMessage()
{
	cout << "num:" << this->num << endl;
	cout << "name:" << this->name << endl;
}

        在下一节中,我将会讲述类的默认构造函数相关的内容。

  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
VS2010中使用C++创建使用DL.docx,文档加代码,全了。工程代码下载: 1.生成动态链接库(_declspec(dllexport)方式导出函数) 2.生成动态链接库(以.def文件(模块定义文件)方式导出函数) 3.以加载时动态链接方式调用DLL 4.以运行时动态链接方式调用DLL 5.以模块定义方式(.def文件)建立的动态链接库的调用 遇到的问题: 1.库导入的时候目录的问题。对应文中的问题1,后面有解释。 2.字符集的问题(是Unicode字符集还是多字节集),两种方案,一种修改字符集为多字节集,二是将字符串前面加 _T(""),文中问题2 3.不知道怎么通过模块定义文件方式生成DLL,通过看参考博客的代码找到了答案,主要修改头文件,和添加模块定义文件。 4.模块定义文件中的库文件名应和工程名一致。 DllMain函数 Windows在加载DLL时,需要一个入口函数,就像控制台程序需要main函数一样。有的时候,DLL并没有提供DllMain函数,应用程序也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的默认DllMain函数本,并不意味着DLL可以抛弃DllMain函数。 根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数,这就说明不能在客户端直接调用DllMain函数,DllMain函数是自动被调用的。 DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DllMain函数也被调用。参数ul_reason_for_call指明了调用DllMain的原因,有以下四种情况: DLL_PROCESS_ATTACH:当一个DLL被首次载入进程地址空间时,系统会调用该DLL的DllMain函数,传递的ul_reason_for_call参数值为DLL_PROCESS_ATTACH。这种情况只有首次映射DLL时才发生; DLL_THREAD_ATTACH:该通知告诉所有的DLL执行线程的初始化。当进程创建一个新的线程时,系统会查看进程地址空间中所有的DLL文件映射,之后用DLL_THREAD_ATTACH来调用DLL中的DllMain函数。要注意的是,系统不会为进程的主线程使用值DLL_THREAD_ATTACH来调用DLL中的DllMain函数; DLL_PROCESS_DETACH:当DLL从进程的地址空间解除映射时,参数ul_reason_for_call参数值为DLL_PROCESS_DETACH。当DLL处理DLL_PROCESS_DETACH时,DLL应该处理与进程相关的清理操作。如果进程的终结是因为系统中有某个线程调用了TerminateProcess来终结的,那么系统就不会用DLL_PROCESS_DETACH来调用DLL中的DllMain函数来执行进程的清理工作。这样就会造成数据丢失; DLL_THREAD_DETACH:该通知告诉所有的DLL执行线程的清理工作。注意的是如果线程的终结是使用TerminateThread来完成的,那么系统将不会使用值DLL_THREAD_DETACH来执行线程的清理工作,这也就是说可能会造成数据丢失,所以不要使用TerminateThread来终结线程。以上所有讲解在工程DLLMainDemo(工程下载)都有体现。 函数导出方式

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值