目录
inline对于编译器仅仅是个建议,最终是否成为inline,取决于编译器,很多情况下会被编译器否决
宏函数
适用于短小、频繁调用的函数
优点---不需要建立栈帧,提高调用效率
缺点---复杂,容易出错,让可读性变差,不能调试(预处理阶段就发生了替换)
#define Add(x,y) ((x)+(y))*10 不用加;分号
记住一定要加括号
内联函数
适用于短小、频繁调用的函数
长函数改成内联会导致代码膨胀;
加个inline;
inline int add(int x,int y)
{
return (x+y)*10;
}
函数如果过长,用inline,会导致可执行程序变大(例安装包变大)
inline对于编译器仅仅是个建议,最终是否成为inline,取决于编译器,很多情况下会被编译器否决
1、比较长的函数
2、递归函数
3、默认debug模式下,inline不会起作用,不然不方便调试
如下两个程序对比图:
第二个程序没有call调用函数
imul相乘指令
内联函数不能声明和定义分离,
分离会存在链接问题,因为没有地址,不会进入符号表,内联和函数直接在用的地方展开
Func.h
#include<iostream>
using namespace std;
inline void f(int i);
test.cpp
int main()
{
f(10);
return 0;
}
Func.cpp
void f(int i)
{
cout << i << endl;
}
Func.h
#include<iostream>
using namespace std;
inline void f(int i)
{
cout << i << endl;
}
test.cpp
int main()
{
f(10);
return 0;
}
关键字auto
自动匹配类型(编译器在编译期间推导而得),使用auto定义变量时,必须对其进行初始化
auto e;(无法通过编译)
int x=10;
auto a=&x;
auto*=&x;//指定类型为指针
auto&=x;
cout<<typeid(a).name<<endl;打印类型
关键字nullptr==((void*)0)
在cpp中,空指针用nullptr表示
cpp中,NULL被替换成了0
开发语言特点:向前兼容,有问题但也需要留下,不然会导致别人的程序出bug
不用形参时可以不写,int x
接收但不使用,可以仅仅只是为了匹配类型
#include<iostream>
using namespace std;
//不用形参时可以不写,int x
//接收但不使用,可以仅仅只是为了匹配类型
void f(int)
{
cout<<"int"<<endl;
}
void f(int*)
{
cout<<"int*"<<endl;
}
int main()
{
f(0);
f(NULL);
f(nullptr);
return 0;
}
类和对象
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
外卖系统
面向过程
上架
点餐
派单
送餐
面向对象
商家
骑手
用户
对象和对象之间的关系和交互
类也是一个域,一般C++中有{}就相当于一个域
#include<iostream>
using namespace std;
typedef int DataType;
//类也是一个域,虽然Init函数名相同,但是它们是在不同的域中,一般情况下Cpp有这个{}括号都是一个域
struct Queue {
void Init()
{
}
};
//成员变量可以写在成员函数之后,也可以在之前,因为类域是一个整体
struct Stack
{
//成员函数 函数名不需要加前缀名称 StackInit
void Init(size_t capacity=4)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_top = 0;
}
void Push(const DataType data)
{
// 扩容
_array[_top] = data;
++_top;
}
DataType Top()
{
return _array[_top- 1];
}
void Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_top = 0;
}
}
//成员变量
DataType* _array;
size_t _capacity;
size_t _top;
};
int main()
{
struct Stack st1;//C
Stack st2;//C++
st2.Init();
st2.Push(1);
st2.Push(2);
st2.Push(1);
st2.Push(2);
cout << st2.Top() << endl;
st2.Destroy();
return 0;
}
类的定义
struct与class两者的区别
struct默认为公有;(因为struct要兼容C)
class默认为私有;
三种限定访问符
只要是类都有访问限定符
public(公有);pubilc修饰的成员在类外可以直接被访问
protected(保护);
private(私有);
1. public修饰的成员在类外可以直接被访问
类的两种定义方式:
1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内
联函数处理;
class Stack
{
public:
//成员函数 函数名不需要加前缀名称 StackInit
void Init(size_t capacity = 4)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_top = 0;
}
void Push(const DataType data)
{
// 扩容
_array[_top] = data;
++_top;
}
DataType Top()
{
return _array[_top - 1];
}
void Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_top = 0;
}
}
private:
//成员变量
DataType* _array;
size_t _capacity;
size_t _top;
};
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
Func.h
#include<iostream>
using namespace std;
typedef int DataType;
//struct Stack
class Stack
{
public:
void Init(size_t capacity = 4);
void Push(const DataType data);
DataType Top();
void Destroy();
private:
//成员变量
DataType* _array;
size_t _capacity;
size_t _top;
};
Func.cpp
#include"Func.h"
void Stack::Init(size_t capacity = 4)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_top = 0;
}
void Stack::Push(const DataType data)
{
// 扩容
_array[_top] = data;
++_top;
}
DataType Stack::Top()
{
return _array[_top - 1];
}
void Stack::Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_top = 0;
}
}
一般前加_或后加_或尾加个m表示为成员变量
成员变量命名规则的建议:
// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
void Init(int year)
{
// 这里的year到底是成员变量,还是函数形参?
year = year;
}
private:
int year;
};
// 所以一般都建议这样
class Date
{
public:
void Init(int year)
{
_year = year;
}
private:
int _year;
};
// 或者这样
class Date
{
public:
void Init(int year)
{
mYear = year;
}
private:
int mYear;
}
面向对象三大特性:封装、继承、多态
封装本质上是一种管理,让用户更方便使用类
局部->类->全局
成员变量不开空间就是声明,定义是整体定义
类的作用域
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout << _name << " "<< _gender << " " << _age << endl;
}
类实例化对象/对象定义/对象实例化
声明和定义的区别在于开不开空间
类里面的成员变量是整体定义的
//int main()
//{
// // 类实例化对象/对象定义
// Stack st1;
// Stack st2;
// //st1.top = 1;
//
// // 不能,类访问top是声明,top不能存数据
// //Stack::top = 1;
//
// return 0;
//}
类的大小(需要内存对齐)
对象的大小只算成员变量,不算成员函数
成员函数是在公共区域,不然每个对象都存,太浪费了
int main()
{
// 类实例化对象/对象定义
Stack st1;
Stack st2;
Stack st3;
Stack st4;
st1.top = 0;
st1.Push(1);
st2.top = 1;
st2.Push(1);
// 对象中只存储的成员变量,没有存储成员函数
cout << sizeof(st1) << endl;
return 0;
}
空类与只有成员函数的类
没有成员变量的类对象,需要1byte,是为了占位,表示对象存在
如果字节为0,那么它们连地址都没有,无法定义对象
不存储有效数据
#include<iostream>
using namespace std;
class A1
{
public:
//成员函数
//默认构造函数
A1(int a = 1)
{
_a = a;
}
void printf()
{
cout <<this->_a << endl;
}
//成员变量
private:
int _a;
};
class A2 {
public:
void f1() {}
private:
char _ch;
//int _a;
double _d;
};
//类中什么都没有——空类
class B
{
};
// 类中仅有成员函数
class C
{
void printf()
{
cout << "C" << endl;
}
};
int main()
{
//无传参,初始化默认为缺省值
A1 a1;
//有传参,初始化为传参值
A1 a2(2);
a1.printf();
a2.printf();
A2 a21;
A2 a22;
//sizeof(对象)和sizeof(类)大小相同
cout << sizeof(a21) << endl;
cout << sizeof(a22) << endl;
cout <<"A2:" <<sizeof(A2) << endl;
/* 没有成员变量的类对象,需要1byte,是为了占位,表示对象存在
如果字节为0,那么它们连地址都没有,无法定义对象
不存储有效数据*/
//空类
cout <<"B:"<< sizeof(B) << endl;
// 类中仅有成员函数
cout <<"C:"<< sizeof(C) << endl;
B b1;
B b2;
C c1;
C c2;
cout << &b1 << endl;
cout << &b2 << endl;
cout << &c1 << endl;
cout << &c2 << endl;
return 0;
}
内存对齐
从上面A2的大小,我们来讨论另一个知识点,内存对齐
还有一个知识点大小端
This指针(暗箱操作)
this指针不允许在形参和实参中显示使用,但允许在成员函数里面写
this指针是一个形参,它存在栈区里
下面我们再来看几种错误写法
看两个代码正确认识this指针
const
Date*const this 无法改this,可以改this里面的内容
Date const*this 和const Date*this 可以改this指向,不可以修改里面的内容
一个错误认识
表达式左边必须为可修改的左值代表其为不可修改的常量
C和C++实现栈对比