标准输入输出
c++输入输出
c标准输入输出:printf,scanf
c++标准输入输出(流):以字节为单位,连续不断的往一个方向流;
输入流:cin>> 要输入的内容(><指向哪侧,就向哪边流)
输出流:cout << 要输出的内容
换行符:end line == '\0'(endl)
优点:不需要规定输入输出格式,使用方便;
引用类型
引用
作用:用来弱化指针,因为引用可以实现和指针一样的效果,并且使用方便,减少临时空间的分配(定义指针需要分配空间,定义引用不需要);
引用:给已经有的变量起个别名,他和变量指的是同一块内存空间(地址也相同);
定义引用:int a = 10; int &b = a;(给a取别名为b)
拓展:const 修饰引用后,引用对象的值不可修改;应用在函数参数设计,保证数据传输的过程中不被修改;const修饰后就变成了常量,类似于宏定义
引用和指针的区别
引用一定要初始化;指针可以不初始化
引用不可以作用于多个变量,只能初始化一次;指针可以指向不同的地址
引用不需要开辟空间,与引用对象公用地址;指针需要开辟空间,有自己的地址
函数重载
函数重载:函数名相同,功能相似,函数的参数不同(不同指:类型,个数,顺序)的一组函数互为重载
函数调用本质:函数名就是函数在内存中的地址
gcc编译后: 函数名字还是add,没有变化,调用的地方函数名不会重命名;
g++编译完之后,函数名会进行重新命名了,_Z3addii,名字与形参有关。在调用的时候就很容易区分执行的是哪一个函数了!
_Z3addii,名字与形参有关(_Z3+函数名+形参首字母;如:void add(int , double)==_Z3addid)
总结:同一个.c文件,使用gcc编译函数名不会发生变化;g++编译会根据函数形参对函数进行重命名
参数默认值
定义函数的时候给参数赋值,就表示该参数未接收到数据时,默认使用参数默认值
所有参数都给默认值的话,调用会变得非常灵活;
默认值的顺序:从右向左
当函数声明和定义同时存在的话,默认值只能存在于一个地方
如何在C++中引入C的自定义库
制作一个自定义的库
x需要创建一个 .c文件和.h文件
使用不同编译器时,需要修改文件内容才能进行编译
预期目标:自动识别使用的编译器,完成编译;
判断是什么编译器
方法1:通过sizeof一个字符,判断是gcc还是g++,gcc的字符大,g++的字符小
#include
int main{
printf("sizeof(char) = %ld\n",sizeof('a'));}
方法2:在头文件中用语句判断是gcc编译还是g++编译;
#include
int main{
#ifdef __cplusplus
printf("g++\n");
#else
printf("gcc\n");
#endif
return 0;}
实际使用:
#ifndef _MAIN_H
#define _MAIN_H
//判断是否g++编译
#ifdef __cplusplus
extern "C"
{
void fun1(int a);
void fun2(int a,int b);
}
#else //若不是g++
void fun1(int a);
void fun2(int a,int b);
#endif
#include
#endif
动态内存分配
c++和c内存分配区别
malloc、free和new、delete区别:
malloc、free是c标准库中的函数;而new和delete是c++中的运算符;
malloc需要开辟空间、告知开辟大小;new不需要开辟空间,后面加上类型就行;
malloc分配出来的空间地址是void*类型,一般需要强制类型转换;new不需要转换;
malloc开辟多个空间(连续的内存空间)用malloc(sizeof(int)*n));
new用 :new int[n]; delete用:delete []s;
c++开辟内存空间
new运算符开辟空间
int *s = new int;
int *s = new int[5]();
中括号代表要申请多大的连续空间,小括号代表初始化为0;
#include
#include
using namespace std;
int main()
{
// c语言
int* p = nullptr;
p = (int*)malloc(sizeof(int));
*p = 100;
free(p);
// c++
int* s = new int;
int *s = new int[5];
return 0;
}
delect运算符释放空间
delete []s;
#include
using namespace std;
int main()
{
// c++
int* s = new int;
int *s = new int[5];
delete []s;//释放new申请的空间
return 0;
}
面向对象编程中的类和对象
面向对象编程的特性:抽象、封装、继承、多态;
抽象:根据多个对象,观察他们的共有属性和对应的行为,并提取出来;
封装:把提取出来的共性封装起来,得到一个整体;
结构体和类
c结构体:为了描述一类事物的属性而自定义的数据类型,是集合的格式
#include
struct student
{
int id;
char name[20];
int age;
void sport()
{
printf("sport......\n");
}
void study()
{
printf("study......\n");
}
};
int main(int argc, const char *argv[])
{
struct student stu={1001,"gqj",23};
printf("%d %s %d \n",stu.id,stu.name,stu.age);
return 0;
}
c语言的结构体中不能有函数存在,c++结构体中可以有函数;
#include
struct student
{
};
int main(int argc, const char *argv[])
{
struct student stu;
printf("%ld\n",sizeof(stu));
return 0;
}
空结构体在c语言中占内存大小为0;c++占内存大小为1;
类:把属性和行为整合在一起描述一类事物的自定义数据类型,在c++中把这种数据类型称为类;
结构体和类的区别
定义变量时,初始化的方式不同;
结构体通过.可以直接访问内部变量,结构体中的成员都是public(公有的)权限,
类也可以通过.获取内部数据,但是类中成员权限默认是private(私有的),若想访问,需要添加权限;
类有访问成员的限定符:public,private,protected(受保护的);
public:共有的,在类内部外部都可以访问,{}以内是类内,{}以外是类外
private和protected只能在内部访问,外部不可访问
类和对象
类定义出来的变量,不叫变量,叫对象;
对象:使用这个自定义的数据类型定义出来的变量;
类和对象的关系:
类是对象的抽象;对象是类的具体实现;
封装
面向对象编程的特性:抽象、封装、继承、多态;
抽象:根据多个对象,观察他们的共有属性和对应的行为,并提取出来;
封装:把提取出来的共性封装起来,得到一个整体;并且要对外提供一些接口供我们外部调用;
总结封装:把属性和行为整合起来,外壳用类这个自定义数据类型封装起来,该隐藏的隐藏起来,该暴露的暴露出来;(一般属性都是隐藏起来的(private,protected),行为都是暴露出来(public));
面试题:对封装的理解
定义一个数据类型是不占用内存空间的,但是它本身是有大小的,用sizeof可计算大小,只有在定义变量的时候才会分配内存空间;
以手机为例,定义类及创建对象
类和对象在内存中的关系:所有的这个类的对象共用一份成员函数,这个类的所有对象都拥有一份自己的数据成员,创建对象的时候,其实是给数据成员分配内存空间的;
this指针
多个对象都去调用同一个成员简述,为还是你们能获取到对象自己的信息?
解决:this指针
this指针:this就是这个的,指针是保存地址,保存这个类对象的地址;;哪个对象调用成员函数,this就指向哪个对象的地址
构造函数
构造函数的作用
是一个对数据成员进行初始化的函数,创建这个对象的时候系统帮忙调用的,当自己没有定义构造函数的时候,系统会有一个默认的构造函数,自己定义构造函数后,系统将不再提供默认构造函数;
构造函数格式
函数名和类名是一样的,没有函数类型,参数可以有
析构函数
析构函数的作用:做清理工作的函数,回收对象所占的内存空间;在对象生命周期结束的时候,系统来调用,析构函数如果我们自己不定义,系统提供。如果我们自己定义了,系统不提供;如果在构造函数中有在堆区分配过内存空间,记得要在析构函数中释放;
析构函数定义格式:无函数类型 函数名:~类名(){};无参数无返回值
析构函数没有参数,所有不能够重载;
最先创建的对象,最后被释放
拷贝构造:深拷贝和浅拷贝
拷贝构造:类进行赋值的时候,进行了拷贝构造操作;用已经存在的对象初始化一个正在新创建没有定义的情况下系统默认提供;
拷贝构造的格式:函数名和类名一样,有参数并且这个参数是当前这个类类型的引用
static修饰类的成员
static作用:
static修饰全局变量,限制全局变量作用域
static修饰局部变量,延长局部变量生命周期
static修饰函数,限制函数作用域
特点:
存储在静态区:.bss ,data
static修饰类的数据成员
类的静态的数据成员属于类,但是不属于具体的某一个对象,但是所有的对象都可以访问;
类的静态的数据成员在编译的时候就已经分配了内存空间,在创建对象的时候不会再给静态的数据成员分配内存空间
fstatic修饰类的成员函数
和类静态数成员访问方式是一样的,也是通过对象和类名的方法去访问
静态的成员函数中 没有this指针,所有访问不到对象的成员
静态的成员函数只能访问静态成员;非静态的成员函数可以访问静态成员
应用
const修饰类的成员
const只读
const int a = 10;
const int *p = &a;*p不可修改
int *const p = &a;p不可修改
const修饰的数据成员
const修饰数据成员,这个成员变量就不可修改了
const修饰的成员函数
const修饰的类对象,智能调用const修饰的成员函数
const修饰成员函数本质上修饰的是this,这样子参数的类型是与普通成员函数类型不一样的,可以有重载
mutable修饰符
mutable修饰符:当使用const修饰了类的成员函数,想去修改类的数据成员,就可以用mutable修饰类的数据成员,该成员就可以修改了(不建议大量使用,尽量少用)
友元
友元就是相当于朋友一样;
关键字:friend
作用:因为我们类的私有的数据成员在类的外部不能直接访问,需要在public区提供对应的接口函数;如果是偶尔要获取到类的数据成员的值,调用函数可行;如果是频繁的获取到对应的数据成员,就要频繁的调用函数;那调用函数需要进行出入栈,是需要开销的,这样子效率也比较低!
可直接访问的方法:
类外的函数做友元,
外部的类做友元,
外部类的成员函数做友元
外部的函数要频繁的访问类的数据成员
运算符重载
成员函数实现运算符重载的方法:
1.成员函数进行重载:this能代表一个操作数,只需要给第二个操作数就可以,所有成员函数重载只有一个参数
2.友元函数进行重载:友元函数没有this,所有需要两个参数;
格式:类名 operator 运算符(参数){}
模板
模板:数据类型参数化
解决问题:代码逻辑冗余减少,只需一个模板可以代替多个函数
模板分类
函数模板
本质不是函数,在内存空间不占内存空间的,只有在调用的时候才会根据给的数据类型生成对应的函数(系统根据输入类型自动生成的)
格式:template(一般用大写表示,typename可以用class代替)
T 函数名(T 参数名,T 参数名)
{}
类模板
链表类
泛型编程==模板的体现==数据类型参数化
标准模板库
stl中的类模板:容器类:
vector:结构和数组是一样的,不同的是,向量是不定长度的,可以随着程序的扩大或者缩小,当容量不够了,会按照1.5倍或者两倍的方式去内存中寻找一块连续的空间,把之前系统中的数据拷贝到新空间中,然后把之前的存储空间还给系统
访问:通过索引的方式访问
案例:封装界面的类
List
#include
#include
#include
using namespace std;
int main()
{
list strList;
//list strList;
strList.push_back("hello");//尾插
cout << strList.size() << endl;
strList.push_front("every one");//头插
list::iterator it = strList.begin();//按位置插入
it++;
strList.insert(it, "teacher");
for (it = strList.begin(); it != strList.end(); it++)//迭代器访问
{
cout << *it << endl;
}
strList.pop_front();//删除
it = strList.begin();
it++;
strList.erase(it);
strList.clear;//容器销毁会自动释放占用的空间
}