欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

类和对象(一)

程序员文章站 2022-07-15 17:04:07
...


前言

本篇文章将带大家进一步了解C++的基础知识,本节将介绍一些C++特性并入门类和对象。


1.内联函数

概念:以inline修饰的函数叫做内联函数,编译时时C++编译器会在·调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。

特点

  • inline本质是一种空间换时间的做法,省去调用函数的时间开销。所以当代码很长的时候或者有循环/递归的函数不适宜使用作为内联函数。
  • inline 对于编译器而言只是一个建议,执行不执行由编译器判断,如果inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
  • inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

2.宏的优缺点

宏:用#define 出来的东西

宏常量:#define MAX 100

宏函数:#define MAX(a,b)a>b ? a : b

:编译预处理指令,在编译预处理时进行且只进行简单的字符替换。而宏定义的函数和用户定义的函数在使用时有如下区别:

  • 1.宏函数在编译预处理时展开,只占编译时间,函数调用则会占用运行时间(分配单元,保存现场,值传递,返回),所以执行相对宏会比较慢。
  • 2.在函数调用时,先得到实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。
  • 3.函数调用是在程序运行时处理的,分配临时的内存单元;而宏展开则是在编译时进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有返回值的概念。
  • 4.对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换;而宏不存在类型的问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定字符即可。宏定义时,字符串可以是任何类型的数据。
  • 5.宏函数的定义也有缺点,它容易产生二义性

类和对象(一)

我们的本意是计算(3+3)*(4+4),结果应该是48,但宏函数只是进行了简单的字符替换,实际计算结果是

3+3 * 4+4=19。

宏的优点:

  • 增强代码的复用性
  • 提高性能

宏的缺点

  • 不方便调试,因为预编译阶段就完成了替换
  • 导致代码可读性差,可维护性差,容易误用
  • 没有类型安全的检查

C++中可以替代宏的技术:

1.常量定义换用const

2.函数定义换用内联函数

3. auto关键字

在C++11中,标准委员会赋予了(auto全新的含义即:auto不再是一个类型存储指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译时期会将auto替换为变量实际的类型。

3.1 auto的使用方法

1. auto 与指针和引用结合起来使用

用auto声明指针类型时,用auto和auto*没有任何区别,但是auto声明引用类型时则必须加&

 int x = 10;
 auto a = &x;
 auto* b = &x;
 auto& c = x;

2. 当同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

void Test_Auto()
{
 auto val1 = 1, val2 = 2; 
 auto cur1 = 3, cur2 = 4.0; // 该行代码会编译失败,因为cur1和cur2的初始化表达式类型不同
}

3. auto不能推导的场景

(1)auto不能作为函数的参数

void Test_Auto(auto val)
{}

(2)auto不能直接用来声明数组

void Test_Auto() 
{
	int arr[]={11,22,33};
	auto array[]={12,23,34};//此处编译会报错
}

4. 面向对象和面向过程

面向过程:关心的是完成事情的过程,分析出求解问题的步骤,以函数来驱动。

面向对象:关注对象之间如何来交互,把一件事情拆分成不同的对象,靠对象之间的交互完成。

5. 类

5.1 类的定义

class className
{
 // 类体:由成员函数和成员变量组成
 
}; // 一定要注意后面的分号

类的两种定义方式

(1)声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可以会将其当成内联函数处理。

class Person
{
public:
	void showInfo()
	{
		cout<<_name<<"_"<<_sex<<"_"<<_age<<endl;
	}
public:
	char* _name;
	char* _sex;
	int _age;
}

(2) 声明放在.h文件中,类的定义放在 .cpp 文件中

//person.h文件
class Person
{
public:
	void showInfo();
public:
	char* _name;
	char* _sex;
	int _age;
}
//person.cpp文件

void Person::showInfo()
{
	cout<<_name<<"_"<<_sex<<"_"<<_age<<endl;
}

推荐使用第二种定义方式!

5.1.1 类 class 和结构体 struct 的区别

struct 的成员默认访问方式是public,class的成员默认访问方式是private。

5.2 类的访问限定符和封装

5.2.1 访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用

类和对象(一)

访问限定符特点:

1.public修饰的成员在类外可以直接被访问

2.protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)

3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止

4.class的默认访问权限为private,struct为public

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

5.2.2 封装

将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

**封装的本质是一种管理:**我们把所有类的数据和方法都封装一下,不想给别人看到。我们使用protected/private把成员封装起来。开放一些共有的成员函数对成员合理的访问。

5.3 类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用::作用域解析符指明成员属于哪个类域。

class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
 	cout<<_name<<" "_gender<<" "<<_age<<endl; 
}

5.4 类的实例化

用类的类型创建对象的过程,称为类的实例化

1.类只是一个模型一样的东西,限定了类的成员,定义出一个类并没有分配实际的内存空间来存储它。

2.一个类可以实例化出多个对象,实例化出的对象,占用实际的物理空间,存储类的成员变量。

3.类实例化出对象就像在现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体建筑的存在,同样类也是一个设计,实例化出的对象才能实际存储数据,占用物理空间。

类和对象(一)

5.5 类对象模型

5.5.1 计算类对象的大小

只保存成员变量,成员函数存放在公共代码段。一个类的大小,实际就是该类中“成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。

结构体对齐规则:

1.第一个成员变量在与结构体偏移量为0的地址处

2,其他成员变量要对齐到对齐数的整数倍的地址处。VS默认的对其数是8

3.结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍

4.如果嵌套了结构体的情况下,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对其数(含嵌套结构体的对齐数)的整数倍。

5.6 this指针

5.6.1 this指针的引出

class Date
{ 
public :
 	void Display ()
 	{
 		cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 	}
 
 	void SetDate(int year , int month , int day)
 	{
 		_year = year;
 		_month = month;
 		_day = day;
	 }
private :
 	int _year ; // 年
 	int _month ; // 月
 	int _day ; // 日
	};
int main()
{
 	Date d1, d2;
 	d1.SetDate(2018,5,1);
 	d2.SetDate(2018,7,1);
 	d1.Display();
 	d2.Display();
 	return 0;
 }

Date类中有SetDate与Display两个成员函数,函数体中没有关于不同对象的区分,那当s1调用SetDate函数

时,该函数是如何知道应该设置s1对象,而不是设置s2对象呢?C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

5.6.2 this指针的特性

1.this指针的类型,类类型const

2.只能在“成员函数”的内部使用

3.this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不需要存储this指针

4.this指针是成员函数第一个隐含的指针形参,一般情况下由编译器通过ecx寄存器自动传递,不需要用户传递。

总结

以上就是今天内容,希望大家有所收获。