C++基础部分
一,内联函数
1,用inline修饰的函数叫内联函数,编译时c++编译器会在调用的时候展开内联函数,这样就不需要建立栈帧了,提高效率。
2,inline对编译器只是一个建议,也就是说,编译器可以选择不展开内联函数。inline适用于频繁调用短小的函数。对于递归,代码相对多的一些函数,加上inline编译器会自动忽略掉 。
3,c语言中的宏定义就是在预处理时替换展开,但是宏定义容易出错,所以c++用内联函数来代替宏定义使用。
4,vs编译器的debug版本下,默认是不展开inline的,这样方便调试,debug版本下想展开需要设置一下两个地方。
#include <iostream>
using namespace std;
inline int Add(int x, int y)
{
return x + y;
}
int main()
{
int ret = Add(1, 2);
cout << ret << endl;
}
这段代码运行后转到汇编指令下:
这样就减少了函数栈帧的建立。
5,inline 声明和定义不能分离到两个文件,分离会导致连接错误。因为inline被展开,就没有函数地址,链接时会报错。因为内联函数是直接展开的,默认是没有地址的。
源码
#include "test.h"
#include <iostream>
using namespace std;
int main()
{
int ret = Add(1, 2);
cout << ret << endl;
}test.h声明
inline int Add(int x, int y);
tpp.h定义
int Add(int x, int y)
{
return x + y;
}
总的来说,内联函数就是一种建议,你写了编译器也不一定会用,这个取决于编译器。如果内联函数可以随便用,那么所有函数都可以展开,都不用建立栈帧了。所以写了内联函数,编译器会判断合不合适使用。
二,nullptr
nullptr即为空指针,也就是在c语言中使用的NULL。由于在c++中,NULL被宏定义为0,所以在有些场景下会产生歧义。
NULL会转化成0,而nullptr不会,它只能转化成指针。所以c++中用nullptr解决这种问题。
//c语言支持,c++不支持
void* p1=NULL;
int* p2=p1;
//c++中
void*p1=nullptr;
int* p2=(int*)p1;
类和对象部分
1,类的定义
1.1类定义的格式
class classname
{
//由成员函数和成员变量组成
};
(1)class为定义类的关键字,classname为类名, {}中为类的主体。类中的变量称为类的属性或类的成员变量,类中的函数称为类的方法或成员函数。
(2)为了区分成员变量,一般在成员变量前加上_,比如_i。
(3)定义在类中的成员函数默认是内联函数。但是是否展开还是取决于编译器。
(4)c++中struct也可以定义类,c++兼容c语言的用法,同时struct升级成了类,与class定义类相比,struct中的成员属性默认是public,而class默认是private.
1.2访问限定符
(1)c++一种实现封装的方式,用类将对象的属性与方法结合起来。通过访问权限,选择性的将其接口提供给外部的用户使用。
(2)public修饰的成员在类外可以直接访问,private和protected修饰的成员在类外不可直接访问。
(3)class没有被访问限定符修饰的默认时private,struct 默认是public.
class stack//定义一个类,类名为stack
{
public://成员函数,类外可直接访问
void push()
{}
void pop()
{}
int top()
{}
private://私有,类外不可直接访问
int* _a;//成员变量名前加上_,便于区分
int _top;
int _capacity;
};
int main()
{
//与结构体不同的是,类名就是一个类型
//而在结构体中,需要加上struct才是一个类型
stack st;//st为定义的对象
st.push();
st.pop();//调用成员函数
//st._top;//不可直接访问
}
1.3类域
类定义了一个新的作用域,类的所用成员都在类的作用域中,在类外定义时,需加上::域作用操作符来指明该变量在哪个域中。
class Date
{
public:
void Init(int year, int month, int day);
private:
int _year;
int _month;
int _day;
};
void Date::Init(int year, int month, int day)//类外定义
{
_year = year;
_month = month;
_day = day;
}
2,实例化
2.1实例化概念
(1)用类类型在物理内存中创建对象的过程,称作类的实例化。
(2)类实际上只是一个模型,类内成员变量都没有分配空间,只是声明。用类实例化出对象后,才会分配空间。
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
//成员变量的声明
int _year;
int _month;
int _day;
};
int main()
{
//Date类实例化出对象d1,d2
Date d1;
Date d2;
d1.Init(2024, 7, 25);
d1.print();
d2.Init(2024, 7, 15);
d2.print();
return 0;
}
2.2对象大小
(1)对象中的存储方式,对象中只存储成员变量,成员函数存放在公共代码区。
(2)计算对象的大小,和结构体一样,都遵循内存对齐规则。
a.第一个成员在与结构体偏移量为0的地址处。
b.其他成员变量要对齐到某个数字(对齐数)的整数倍地址处。
c.对齐数=编译器默认的对齐数与该成员大小的较小值。
d.结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐数取最小)的整数倍。
e.如果嵌套了结构体,嵌套的结构体对其到自己的最大对齐数的整数倍处。
(3)计算Date类的大小
(4)当类中没有成员变量时,对象的大小是1
2.3this指针
(1)在上面的Date类中,有Init和print两个函数,在我们调用的时候就不知道是要访问d1的数据还是d2的数据,所以这里需要使用c++中隐含的this指针来解决问题。
(2)编译器编译后,在每个成员函数的第一个形参位置,会默认加上一个当前类类型的指针,叫做this指针。
class Date
{
public:
//void Init(Date* const this,int year, int month, int day)
void Init(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
//void print(Date* const this)
void print()
{
cout << this->_year << "年" << this->_month << "月" << this->_day << "日" << endl;
}
private:
//成员变量的声明
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.Init(2024, 7, 25);//相当于d1.Init(&d1,2024,7,25)
d1.print();//相当于d1.print(&d1)
d2.Init(2024, 7, 15);//d2.Init(&d2,2024, 7, 15)
d2.print();//d2.print(&d2)
return 0;
}
注:形参的位置不可出现this指针,但在函数体内可以使用this指针。