前言
c语言是结构化语言,c++兼容c,但是c不兼容c++。
c语言注重实现逻辑,c++注重过程,整体设计。
c语言对于大项目不易于维护、扩展。
c++考虑整体优化和管理。
c++头文件一般没有扩展名,.h一般是c语言的,c开头的是c++的。
c++标准函数库和类库的合体。
std是命名空间,所有组件的名字都存在std中,打开命名空间才可以使用形如cout等命令。
可以用std::cout<<这种方式,但是打开了namespace也就没必要了。
c++组成:c语言+面向对象+泛型编程+STL标准模板库。
面向对象三大特性:封装、继承、多态。
- 封装:零散的数据和算法,放入一个集合,方便管理使用。
- 复用:功能体现在方法中,针对功能去设计。
- 扩展:新的功能不能影响原来的功能。
可以用模板类实现复用。
template<typename T>
T add(T a,T b){
return a+b;
}
可以对同一类型进行模板代用。
解决方案中可以直接添加项目sln。但是只能同时调用一个,选为主要的。
debug调试信息都在,release没有调试信息,更小一点。
因此除了调试信息的内容,剩下都必须留下。工程文件和cpp等都留下。
作用域
::表示作用域运算符。
<<输出操作符,>>输入操作符,endl有刷新缓存的用法,都是函数。
cout是一个ostream类的对象。
变量在程序中所起的作用范围:全局、语句、局部,语句指的是临时变量,一般没用。
作用域优先级:范围越小越优先。
作用域运算符作用:区分同名成员,例如cout<<::n,就是全局区的n变量,而不是局部的。
重定义:同一作用域不可能有两个变量名重复。
可以划分作用域,即可使用重名。
命名空间:组织和重用代码编译单元,解决重名现象。
using+空间名字,或空间名+作用域运算符。
命名空间目的:整体操作方便,实现:复用+扩展。
namespace AA{int a=10}也可以对空间内容直接显示,using AA:a。
#ifdef#endif
#ifdef AA
using namespace aa;
#endif
#ifdef BB
using namespace bb;
#endif
#define AA
cout<<a<<endl;
此时的a就是aa空间里的a了。
new/delete
new数组,int* arr=new int[10];delete[] arr;
new int[10](),就都是初始化空值。其中()不是初始化列表,{}才是。
注意数组的类型是int[10],并且delete时候也要表示出这个变量是数组。
arr=0,表示数组是空。因此可以用if(!arr)来作为条件判断。
指针delete之后,一定要继续p=0;依次来将指针所指赋空。理由是:避免内存泄漏。
new int(123),初始化地址中内容,是123。
指针数组,他是数组,但是里面都是指针,也就是说内容是,int*[],但是如果new的话,他要用的是首元素来接住,那么首元素就是int*,然后指针还要加一个*,就是int** p=new int*[];
数组指针,是一个数组的指针,new (int(*)[]),然后接的时候,应该就是整个数组,int(**p)[]。
二维数组元素都是一维数组,int(*p)[]=new int[][];
new申请类对象,会调用构造函数,而malloc不会。
重载
for基于数组有特殊用法,for(auto& x:arr),即可在不使用下标的情况下,直接遍历数组,而其中的x表示每一个元素,依次进行赋值。
这里利用了引用,表示可以直接取值,在函数中用的颇为广泛,也就是说,可以全部加上。
缺省函数参数,可以在函数中使用默认值,这样,在调用的时候可以不用加入参数。
参数不可以传一半,形参只能是从后往前进行传默认值,而且函数调用时,不能是空参数。
同名函数不可以同时出现,但是,如果是参数不同的话,那可以。
这种同名函数之间的关系叫做:重载。
重载:当同一类功能的函数,只是部分细节不同,说的是参数类型和个数,利用“重载机制”可以将这些函数去程相同的名字,从而使程序易于阅读和理解。
引用
引用:变量的别名。
类似于指针,两个名字指向一个空间。引用定下来之后,就永远是这里了。
引用好处:避免将整块数据全部压栈。提高程序效率。不会新申请空间。
返回值是引用的好处:不会产生返回值的副本。
方法:必须在定义的时候初始化,int& b=a;而且后面不能是常值,必须是变量。
常引用:const int& b=123;
与引用不同,后面就是常值。
void (p_fun)(int)=&show;
STL-string
bool值只占1字节,小写true和false。
string是类,里面有很多的使用方法,如链接所示:【C++】STL-string
链接:【C++】STL-string_王向晚的博客-优快云博客
字符串名字代表地址。
类与对象
面向过程:DS+算法。
面向对象:对象求和。而对象=数据+算法。
类:复杂数据类型。将不同类型的数据和相关操作封装在一起的操作。
class的结尾有分号!
private一般是用于接口类中,不可访问的成员。如果继承可以用protected。
如果拥有接口,甚至可以修改private的内容。
类中的变量,不可以直接初始化。
类不占用空间,而对象占用。
类中不能装自己类的对象。
显示和赋值要分开。
接口:提高安全性,封装复用。
构造、析构函数
与类同名,无返回值。
目的:给类成员初始化。定义对象的时候自动调用。
如果不写构造或析构,系统会给一个。
但是构造可以有多个(是否有参),但是析构只能有一个。
析构:反向构造,释放空间。由析构来完成内存回收。
作用域结束,调用析构,或者是对象生命周期结束。
类是数据与算法的集合。
析构函数出现在return 0之后。
虽说有析构函数,但是回收的也是类内的空间,但是如果你是new的对象,你要手动析构你自己创建的这个对象。
全局对象:整个应用程序,可以跨文件。跨文件用extern进行仅声明。
静态:程序创建开始,到程序结束。但是静态,仅限在自己文件中使用。
但是如果是同文件之内,第一次创建之后,后面就不会创建了。
封装
优点:可读性强,便于修改维护,构造析构。
接口:当换了操作系统或者编译环境时,无需修改。
设计原则——单一职责:职责越少,复用性越高。
因此要拆分函数,与此类无关的函数不能放入。
对象种类
全局对象生命周期:程序退出。
正常对象:return 0——析构——结束。
全局对象:return 0——结束}——析构。
new和delete触发构造和析构。
堆区空间要手动释放,以免内存泄漏。
this指针
空类占1B,起到占位作用。
空类+函数,也是1B。
但是有成员,例如一个int,就是4B了,而不是4+1B。
成员属性:定义对象时分配空间,每一个对象都有一分。
成员函数:编译期存在,在代码区,所有对象共用一份函数。
因此理解为,即使我们没定义对象,但是可以调用没有成员属性的成员函数。
如果定义的是空对象,那么也不可以对对象成员属性进行赋值。
因为得0就是没空间,1B的占用,但是如果是new的,那么是一个没赋初值的空间。
在类成员函数中,如果有与成员属性同名的变量存在,那么将如何区分呢?
就是this指针,例如都是a,但是在类中可以用this->a来表示,这是成员属性。
this的类型是类的指针。
注意的是:this指的是:对象的地址&obj,也就是说,如果你是空类或者没new,那么this是不存在的。
这也就理解了,静态成员函数,是没有this指针的。
因此静态成员函数,即使没定义对象也是可以使用的。
非静态成员函数,必须通过对象调用。
空对象,只能调用没有成员属性的。
this只存在于函数的参数里,是用来调用对象地址的。使用this完成的封装。
const
const变量:1、定义就要初始化,2、不可以修改。
const*随便指,不能改内容。*const里面随便换,不能改指。
类中的const成员属性,必须通过初始化列表初始化。构造函数的初始化列表在类名()之后,用冒号开始,逗号分隔。
逻辑顺序:创建对象——构造函数——初始化列表——构造中的内容。
类常成员函数,const写在函数名()之后,void show() const。
常函数:可以用普通变量,但是不能修改普通变量,常变量本身就不能修改,无论在哪。
常对象:只能用常函数,不能用普通函数。
常对象可以调用任何属性。
理解为:this指针从*变成了const*类型了,因此就不能修改内容了。降低安全性。
初始化列表
按照变量定义的顺序执行,也就是行数先后,与自己写的顺序无关。
int b;
int a;
lei(int x):a(b),b(x){}
如上代码所示,即使是列表中a写在前面,但是定义时,b先定义的,那么就先执行b(x),然后执行a,这样就不会有乱码。
类中不能定义自己类的对象,但是可以定义自己类的指针,也需要在列表中初始化。
static
静态编译期就存在,每次调用,都是这一个东西。
也就是说,静态成员属性和函数,都可以不用对象就能调用。
静态成员属性要在类外进行初始化,static int a; int lei::a=10;
初始化也要加上成员的类型,也就是先int才行。
静态成员和函数也都不占用内存,要不无法解释无需对象调用这件事。
静态成员函数:制衡使用静态成员。
理由就是没有this指针。因为无需对象调用。
const:能不能改,static:能不能用。
类之间的关系
组合(复合)、依赖、关联、聚合。
part of、use、have、combine。
当对象作为参数,尽量使用引用,如果怕被修改,再加入const。