静心、专注、思考....
目录
- day 6
C/C++中涉及到的知识点:
-
数组
-
指针
-
结构体内存对齐
-
函数调用过程
-
C语言中的动态内存管理
-
进行文件操作常用的API
-
str系列和mem系列函数的模拟实现
-
宏
-
static和extern
-
不定参数的函数
一、内联函数?宏函数和内联函数的区别?C++中有哪些方式可以替代宏?
宏函数的优缺点:
-
优点:在预处理节点,预处理器会将宏函数展开,少了函数调用过程开销---提高程序的运行效率
-
缺陷:不是真正的函数,在预处理阶段展开,因此不参加编译,不方便调试
-
宏函数的副作用:代码膨胀
内联函数:被inline关键字修饰的函数,成员函数放在类中被定义
-
注意inline建议性关键 原则:不能太长,循环,递归
-
在编译阶段,编译器会将内联函数展开--少了函数调用开销
优点:
-
真正的函数,参与编译,对参数进行类型检测--安全性比较高
-
debug模式下,编译器不会展开
缺陷:代码膨胀
宏常量:const类型的常量:在编译阶段,直接替换。
二、类和对象
什么是封装?
1、概念:将一个事物内部细节隐藏,必须暴露一些公有的接口让对象之间进行交互;
2、例子:
class Student{ //类名称
private: //私有属性
string name; //成员变量,名称
public:
Student(string& name){ //构造函数
this->name = name;
}
~Student(){} //析构函数
void Study(){ //成员函数
cout << "学习" << endl;
}
};
函数是封装的一种形式:函数所执行的细节行为被封装在函数本身这个更大的实体中,被封装的元素隐藏了它们的实现细节–可以调用一个函数但是不能够访问函数所执行的语句。
因此,封装相当于就是class+访问限定符。
注意:访问限定符本质上是给编译器使用的,数据放在内存中没有任何限制的。
<1.函数名相同,参数表不同的才叫做函数重载。那两个函数一个是构造,一个是析构,不是函数重载。
<2.C++的封装就是说把一个对象封装成类,并设置成员的访问权限(public公有,protected保护,private私有)学生是一个对象,封装成类Student,类里面有属性和操作,属性就是name,每个学生都有名称吧,操作就是Study,学生就是要学习的,不是吗,而构造函数和析构函数是类必不可少的部分,用于创建对象和回收资源,如果你不写,系统也会给你默认的构造和析构,如果没有复杂的成员的话,系统默认的构造析构也就够用了。
3、C++中如何实现封装:
-
类:将对象的属性和方法包装在一起;
-
访问权限:通过访问权限选择性将方法提供给外部进行使用private/protected/public
4、C++中class和struct的区别?
-
class默认是private,struct默认是public。
-
struct更适合看成是一个数据结构的实体,class更适合看成是一个对象的实现体。
-
class和struct如果定义了构造函数的化,都不能用大括号进行初始化。
5、this指针?
关于this指针的一个经典回答:
当你进入一个房子后,
你可以看见桌子、椅子、地板等,
但是房子你是看不到全貌了。
对于一个类的实例来说,
你可以看到它的成员函数、成员变量,
但是实例本身呢?
this是一个指针,它时时刻刻指向你这个实例本身
-
this指针只能在成员函数内部使用;
-
this在成员函数的开始前构造,在成员函数的结束后清除;
几个常见问题?
1.this指针是什么时候创建的?
答:采用new的方式创建对象的话,在队里分配内存,new操作符通过eax返回分配的地址,然后设置给指针变量。
2.this指针内存放在何处?堆、栈、全局变量、还是?
答:this只会因编译器不同而有不同的位置。(栈、寄存器、全局变量....)
在成员函数中,你是可以知道this指针的位置的(可以通过&this获得),也可以直接使用它。
3.this之如何传递类中的函数?
答:大多数编译器通过ecx寄存器传递this指针。(泪在实例化时,只分配类中的变量空间,并没有为函数分配空间)
4.普通的类函数(不论是成员函数,还是静态函数)都不会创建一个函数表来保存函数指针。只有虚函数才会被放到函数表中。
6、空类的大小?为什么?【定义了一个空的类型,里面没有任何成员变量和成员函数。对该类型求sizeof,得到的结果是多少?】
答:答案是1。空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占多大内存,由编译器决定。(在VS中,每个空类型的实例占1字节的空间)。
<1>如果在该类型中添加一个构造函数和析构函数,再对该类型求sizeof,得到的结果又是多少?
答:还是1。调用构造函数和析构函数只需要知道函数的地址即可,而这些函数的地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加额外的信息。
类中六个默认的成员函数--默认:如果用户没有显示提供,编译器生成一份默认的成员函数。
构造函数:
概念:一个特殊的成员函数,在创建对象时由编译器自动调用,完成对象的初始化工作。
特性:
-
函数名与类型相同,没有返回值类型,在创建对象期间由编译器自动调用,并且在对象的生命周期只调用一次。
-
缺省构造函数:无参构造和全缺省构造,并且只能存在一个。如果用户没有显示提供,编译期将会提供一个;
-
不能使用static和const修饰,不能是虚函数;
-
具有初始化列表:初始化类中的非静态成员变量;
-
作用:构造好初始化对象;
-
单参构造函数--具有类型转化的作用,禁止:explicit
析构函数、拷贝构造函数、赋值运算符重载------如果没有显式实现:编译器生成默认成员函数(浅拷贝)
static成员 || 对static理解
在C语言中:static变量:全局变量 || 局部变量;
在C/C++,
-
可以使用static修饰类成员---静态成员;
-
static修饰成员变量---普通成员变量;
-
是类的属性,不是某个具体的对象;
-
是所有类型对象共享,不会影响sizeof结果;
-
在类中只是声明,必须将其放在类外进行初始化,不能将其放在构造函数初始化列表初始化;
-
访问方式:可以通过对象访问 || 类名::
const成员 || const的理解
在C语言中,const只能修饰变量<这里指不能被修饰的变量>
在C++中,const修饰变量----常量<具有替换>
const修饰类成员:
-
成员变量:不能在成员函数内部修改,必须在构造函数初始化列表位置进行初始化
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:
class CExample {public:
int a;
float b;
//初始化列表
CExample(): a(0),b(8.8)
{}
//构造函数内部赋值<构造函数初始化>
CExample()
{
a=0;
b=8.8;
}
};
-
成员函数:const成员函数,修饰成员函数隐藏你给的this指针;表示:在该成员函数中,不能修改类中的任何非静态的长远变量;【例外:除非该成员变量被mutable修饰】
静态成员函数----普通成员函数
-
本质:静态成员函数没有隐藏的this指针
-
调用:对象.静态成员函数 || 类名::静态成员函数
-
静态成员函数不能访问:非静态的成员变量&&成非静态的员函数
-
静态成员函数不能使用const修饰
-
静态成员函数不能为虚函数;
>面向对象和面向过程的区别?
-
面向过程就是分析出问题解决所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就好了;
举个例子:<五子棋>
面向过程:
-
开始游戏
-
黑子先走
-
绘制画面
-
判断输赢
-
轮到白子
-
绘制画面
-
判断输赢
-
返回步骤2
-
输出结果
面向对象:
-
黑白双方
-
棋盘系统
-
规则系统
面向对象的特点:属性、类
-
属性:用来描述具体某个对象的特征。面向对象的思想就是把一切都看成对象,而对象一般都有属性(对象静态的一面)+方法(对象动态的一面)组成!
-
类:具有同种属性的对象称为类,对象就是类的一个实例化;
面向过程与面向对象的优缺点,举一个生活中的常见例子:
-
用面向过程的方法写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭;
-
盖浇饭的好处就是‘菜’‘饭’分离,从而提高了制作盖浇饭的灵活性---‘可维护性好’,‘饭’和‘菜’的耦合性比较低;
-
蛋炒饭就是将‘蛋’和‘饭’搅拌一起,耦合性很高‘可维护性’比较差。
-
软件工程最求的目标之一就是可维护性,主要表现在3个方面:可理解性、可测试性、可修改性;
面向过程:
-
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,较消耗资源,嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素;
-
缺点:没有面向对象易维护、易复用、易扩展;
面向对象:
-
缺点:性能比面向过程低;
-
优点:面向对象易维护、易复用、易扩展,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使用系统更加灵活、更加易于维护;
>面向对象的三大特性:封装、继承、多态
>c++关键字(98)---63
>命名空间作用:解决命名冲突
>缺省参数:在声明或定义一个函数时,给函数参数设置一个默认值,在调用该函数期间,如果没有传递实参,采用默认值,否则,采用传递的实参
分类:
-
全缺省参数--所有参数都带有默认值;
-
半缺省参数--部分参数带有默认值(只能从右往左提供);
-
注意:半缺省参数只能从右往左依次提供,不能在声明和定义时同时给出;
>函数重载:
-
在相同作用中,具有多个相同名称的函数,参数列表必须不同(个数、类型、类型次序),注意:与返回值类型是否相同无关
-
调用原理:在编译器阶段,对所传递的实参类型进行推演,根据推演的实际结果去选择调用相对应的类型的函数
>C语言和C++编译器对函数名字修饰规则不同
-
C语言编译器名字修饰规则:仅仅在函数名谦增加一个_;
-
C++名字修饰:将类型编译到底层使用的函数名字中;
-
extern "C":按照C语言的风格来编译函数
>引用:引用就是别名,引用类型变量与其引用的实体共用同一块内存空间
特性:
-
引用在定义时必须初始化,并且不能够改变;
-
引用必须同类型;
-
非常量引用的初始值必须为左值;
引用和指针的区别?
-
引用在定义时必须初始化,指针没有要求;
-
引用在初始化时引用一个实体,就不能在引用其他实体了;而指针可以在任何时候指向任何一个同类型的实体;
-
没有NULL引用,但有空指针;
-
在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节数(32位平台下占4个字节,64位平台下占8个字节);
-
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小;
-
有多级指针,但是没有多级引用;
-
访问实体方式不同,指针需要显式解引用,引用编译器自己处理;
-
引用比指针使用起来更加安全(引用必须判空);
>宏的优缺点:
优点:
-
增强代码的复用性;
-
提高性能;
缺点:
-
不方便调试宏;(因为预编译阶段进行了替换)
-
导致代码可读性差,容易误用;
-
没有类型安全的检查;
C++有哪些技术替代宏?
-
常量定义--换成const
-
函数定义--换用内联函数