四大件
- 数据结构和算法
- 计算机网络
- 操作系统
- 设计模式组成原理
数据类型
常量
宏常量 #define
const 修饰变量
均不可修改
字符串
char str[] = “ldksjfa”,会默认多一个’\0’
string str = “dlkjafd” 需要头文件
bool char int float double void wchar_t
typedef为已有类命新名字
typedef int feet;
feet a = 1;
运算符
- 三目运算符可以返回变量也可以返回变量值
控制结构
- if - else if - else
- switch - case break- case break-default break 只能是int 或者 char,必须break
- 循环结构 while(){},do{}while(),for(int i =0;i<10;i++)
- goto FLAG; FLAG:
- continue
数组
一维数组
定义数组
- int a[10];
- int a[10] = {0,1,2,3,4,5,6,7,8,9}
- int a[] = {0,1,2,3}
特点
- 数据类型相同
- 内存空间连续
赋值
int a[10] = {0}; //初始化表长度小于要被初始化的数组的元素数目,剩余元素均为0;
注意
int n;
cin >> n;
int a[n]; //这是错误的写法,尽管有些编译器可能不会报错
int b[0]; //长度为0的数组没有任何意义
int c[10];
cout<<c[11]; //越界但可以输出排序查找
选择排序
冒泡排序
二维数组
初始化赋值
int a[10][10] = {{1,2}, {2,3}, {3,3,4,4}};
int a[][10] = {{},{}}
- 注意
- 行数
- 元素数目(用好sizeof())
- 理解好变量名与地址的关系
字符串
初始化
char ch[] = {'h','e','l','l','o','\0'};
char ch[] = {"hello"};
char ch[] = "hello";
char ch[] = "hello"; //这是错误的
char ch[10] = "hello"; //后面的字符都是'\0','\0'是不可显示的
char ch[6] = {'h', 'e', 'l', 'l', 'o'} //是字符串
char ch[5] = {'h', 'e', 'l', 'l', 'o'} //不是字符串空字符串
char ch[] = ""; //占用一个字节存储'\0'
//'a'与"a",前者是简单的变量,后者是数组遍历
for(int i = 0; str[i]!='\0'; i++)
{
cout<<str[i];
}输入输出
char str[6];
cin>>str; //空格字符会结束输入
cout<<str; //遇到第一个'\0'结束。
cin.get(str, length, endchar); //结束字符默认是'\n',结束字符会留在输入流中
cin.getline(stxr, length, endchar); //结束字符默认是'\n',结束字符不会留在输入流中,读入的字符是length减一,因为'\0'string
strlen(str);//可见字符串长度,不包括'\0'
strcpy(str1, str2); //不能用str1 = str2赋值,str1 要 大于 str2,//字符串排序
strcat(str1, str2); //拼接
strcmp(str1, str2); //比较大小,挨个比较ascii码 返回值是-1,0,1
//以上三个都有n族,即strncpy,strncat,strncmp,即操作前n个字符
strlwr(str); //转换为小写
strupr(str);
//应用
len = strlen(str1);
for(int i = 0; i < len; i++){ //不推荐
cout << str1[i];
}
函数
注意
- 声明可以写多次,定义只能写一次
- scanf返回值是读入的个数,
ctrl Z
返回-1 - cin返回值是
true
或false
定义
定义的时候如果不声明返回值类型,默认是
int
如果return类型和定义类型不同,则强转(以定义为准)
函数不可以嵌套定义,但可以嵌套调用。
传参
- 传入的类型不对时会发强转
变量作用域
- 复合语句内是局部变量
- 复合语句是指大括号内,不一定是循环或者函数,大括号可以随时出现,组成复合语句。
变量存储类型
每个变量都有两种属性,分别是数据类型和存储类型,因此标准的变量定义应该是
标准类型 数据类型 变量名
- auto(自动变量)函数内部或符合语句{}内。
- register(寄存器变量)
- extern(外部变量)指不在此块或此块之前声明过的变量,也可以引用其它文件的变量,另外引用其它文件的函数前面的extern可以省略
- static(静态(外部)变量)该变量不能由extern引用
数组作为参数
int myFun(int arr[], int n){ |
带默认值的函数
- 缺省形参值必须从右向左连续定义(自右向左入栈)
- 如果省掉了某个实参,直到最右边的实参都要省
- 缺省值应该在函数原型就给出
- 同一作用域,不可以重复定义缺省值
- 函数原型给出缺省值时,形参名可以省略
- 同一作用域内的函数原型和函数定义不能同时定义缺省值
内联函数
inline int fun(){}; |
仅在原型出加inline
无效,内联函数的定义必须出现在调用之前,否则就是普通函数
如果函数体积较大或者函数内有循环比较耗时,则不宜用内联
重载函数
- c语言是不支持的
- 仅仅是返回值不同是不行的,编译不通过
- 编译器决定调用哪个函数的过程称之为绑定
- 绑定的优先级:精确->低转高->高转低
- 带默认值的函数等可能引起二议性
函数模板
- 泛型化(类型的参数化)
- 如果模板中有普通类型,如int,则必须显示实例化
|
指针
定义
int a = 10; |
任何类型的指针都占四个字节
空指针和野指针
当指针定义为0、NULL、或者不赋值,此时不能解引用,否则崩溃
- 空指针:指向内存编号为0的指针,一般用来初始换(0-255)不可访问,系统占用。
int * p = NULL
或int * p = 0
- 野指针:指向非法空间(是一种错误),无法访问。
const修饰指针
指针就是一种特殊的变量,专门用来存放地址。地址可以看作一个特殊的数据类型,地址不可以复制给int变量,但可以赋值给指针变量,int的地址,只能复制给int *。地址可以解引用,所以&a和p的用法是一样的。
常量指针
int a = 10; |
指针常量
int * const p = &a; //指向不可以改,解引用可以改。 |
常量指针常量
const int * const p = &a; //都不可以改 |
指针和数组
此处,若那指针p指向数组首地址,那么此时的数组名和指针名的本质其实都是一个指针,访问数组的元素可以通过方括号来访问,也可以用*来访问。需要注意的是p和a的区别,p是变量,但a是常量指针,a不可以进行a++等运算。另外,指针相加无意义,相减有意义。
int arr[3] = {1,2,3}; |
利用指针进行排序,可以避免复杂的例如结构体、字符串的赋值,效率更高
int main() |
指针与字符串
指向字符串常量和指向字符数组的指针有什么区别呢?
char *string; |
要搞清楚string
、string2
、ss
三者的区别:
- *string不可以修改,两外两个可以
- ss不可以修改,两外两个可以
- 三者都属于指针,第一个指向常量即常量指针,第二个正常指针,第三个本身是常量即指针常量
利用指针编程的实例(复制字符串)
//把字符数组a赋值给b |
指针与函数(地址传递)
主要注意传递指针以及返回指针
值传递是文件复制,地址传递是建立快捷方式
int add(int * p1, int * p2){ |
指向函数的指针
用的不多
int f(int a, int b){} |
引用传递
与指针类似,但更安全、简单,主要用于函数的参数,不需要解引用,也不需要传参的时候传递地址
int a = 3; |
返回值为引用类型的引用
int & max(int a[], int n){ |
常引用:不能通过常引用来修改变量的值,所以常引用可以用来引用常量,表达式等,通常用来作为只读形参。传引用更快,但又不希望被改动,所以更安全
int a = 1; |
动态内存
使用
先记录一个常见的错误
int main(){ |
动态数组
int main(){ |
补充
关于内存分配的一些问题
对于三个区域,数据段、栈、堆,全局变量存储在数据段,局部栈,动态分配堆。对于一些很大的例如大数组,不能直接在堆里创建,否则会崩溃,需要在堆里new
,且用完之后一定要delete
否则会不断累积,造成内存泄漏,但要注意delete
之后不允许再操作此处内存;也可以放在数据段里,即定义全局变量。
结构体
定义与使用
struct Student |
结构体数组
struct Student |
结构体指针
struct Student |
结构体嵌套
要在之前声明
结构体传参
面向对象编程
类
private
(成员函数可以访问本类其它对象私有成员)、public
、protected
(该类以及子类)出现的次序和顺序可以任意,默认是private
类本身并不占内存
数据成员
可以使用已定义完整的类,但当前正在定义的类没有定义完整,所以不能实例化
class Grade{ |
函数成员
类内定义的称为内联函数,若要在类外定义,必须在类里声明原型
void Clock::SetTime(int h, int m, int s){ //::是作用域分辨符
... //访问类里变量时直接访问就可以,写法和内联一样
}默认形参可以放在原型里也可以放在定义处,不可都放,一般放原型
只有一份,不会给每个实例都放个函数
构造函数
- 没有返回值类型,且不用void的,一般函数需要void
- 构造函数只能被自动调用
- 构造函数要放在
pubilc
里面,否则无法实例化 - 构造函数可以重载
- 只有在不定义构造函数的时候才会自动生成无参构造函数
- 定义有参构造函数时最好再重载一个无参构造
对象数组
class Score{ |
常量
常量对象
const 类名 对象名; |
- 常对象只能调用常成员函数
- 常对象本身以及其内部的属性等都不可以改变
常成员函数
class A{ |
- 原型和实现都要加
const
const
可以用来重载函数- 不能修改数据成员
- 不能调用非常成员函数
- 普通对象可以调用常成员函数和非常成员函数
常数据成员
定义和普通常量一样(注意:常量声明必须初始化),但是由于老标准中初始化类时不允许赋值,所以可以在构造函数里通过初始化表赋值
class A{ |
静态成员
静态数据成员
静态成员只保留一次拷贝
不能用构造函数初始化静态成员,必须在类外初始化
定义对象时不会为静态成员分配空间
调用时可以通过类调用也可以通过
obj.
调用,值相同
class A |
静态成员函数
不依赖任何对象,用来访问静态数据成员,可以通过类直接调用,也可以通过对象调用。
可以在类内声明原型,然后在类外定义,此时,类外不用加static
普通函数可以访问静态变量,但是静态函数不可以直接访问普通变量,但可以访问自己创建的或是传进来的实例的变量
友元
友元类
友元函数
友元成员函数
对象析构与拷贝构造
析构函数
~构造函数名(); |
析构函数没有重载,没有参数,没有返回值
如果没有定义,系统会生成空析构函数
调用顺序和构造函数相反(出入栈)
拷贝构造函数
//默认拷贝构造函数,可以用旧实例来初始化新实例。 |
在初始化,函数传参,返回值的时候会自动调用拷贝构造函数。系统默认生成拷贝构造函数。但是先声明,再赋值不会调用,而是调用=
。注意一定是传变量的时候才会调用,如果直接传递常量MyClass
默认拷贝构造函数比较傻,只能浅拷贝(即直接赋值),这会导致,如果你的类用到了动态内存,那么两个指针指向会相同,所以需要自己重新定义拷贝构造函数,重新开辟内存。
而当自己重新定义拷贝构造函数之后,默认的拷贝构造函数和构造函数都会消失,所以你还需要定义构造函数。
但是如果你只是重新定义构造函数,默认的拷贝构造函数依旧完好。
运算符重载
构造函数涉及到动态内存一定要注意
- 拷贝构造函数
- 重新赋值运算符
- 析构函数
- C++ 要求’=’、’[]’、’()’、’->’运算符必须被定义为类的成员函数,把这些运算符通过全局函数进行重载时会出现编译错误;
- 如果有一个操作数是类类型(如 string 类),那么对于对称操作符(比如操作符“==”),最好通过全局函数的方式进行重载。
- 如果一个重载运算符是类的成员函数,那么只有当与它一起使用的左操作数是该类的对象时,该运算符才会被调用;而如果该运算符的左操作数确定为其他的类型,则运算符必须被重载为全局函数;
- 有一些运算符是不能重载的,如“sizeof”;
- 不能创建新的运算符;
重载为全局函数
- 最好设置为友元函数
重载为成员函数
- 形参少一个
常见的重载
- =可以返回*this,这样就可以连等了!
- []返回类型要加个&,这样就可以通过[]修改了!
- 前置++,重置为一元,后置为二元(加个新参int x 会默认传0)
- >> << 必须是全局
组合
如果成员类没有无参构造函数,那么组合类的构造函数的时候,必须携带初始化表。**初始化表不是在调用构造函数,构造函数的调用顺序和初始化表无关,它仅仅是提供参数。顺序应该是和数据成员里声明的顺序相同。
class A |
继承
一些名词:继承、派生。基类、父类,派生类、子类 ,单继承,单向继承,多继承,多重继承,多层派生,多层继承
继承决定访问权限上限
派生类不继承构造函数、析构函数、赋值运算符,但是可以调用基类的
构造函数调用顺序:基类、成员对象、自己。
如果基类没有无参构造函数,那么派生类必须定义构造函数,通过参数表传递给基类,即便派生类本身不需要重新定义构造函数,此时重新定义的构造函数作用仅仅是传递参数。如果基类有的话则可以不定义。
对于多层派生,派生类会调用直接基类的构造函数,依次上溯。
继承方式如果不写,默认是private
多继承
class B:public A, public C{ |
基类构造函数调用顺序按照声明的顺序,如例子中先调用A,再C
接着调用成员对象的构造函数
最后派生类
继承默认是private
多继承要时刻注意可能出现二义性。
多态
其实就是为了方便对所有的派生类进行统一管理罢了
- 虚函数不能是静态成员函数,也不能是友元函数,因为二者都不属于对象
- 多态的实现只能是指针或引用(基类指针指向派生对象,或基类引用指向派生对象)
- 派生类继承基类的虚函数,无论写不写
virtual
都是虚函数 - 派生类的重载函数会覆盖继承过来的同名虚函数
虚析构函数
当派生类需要析构去delete动态内存时,如果是基类的指针,只会执行基类的析构函数,为了能够析构派生类,所以加了virtual
。同时派生类会调用基类的析构函数。而基类不会调用派生类的析构函数。
纯虚函数
只有原型没有实体
virtual int f(int, double) = 0;
抽象类
一个类中至少有一个纯虚函数,则称之为抽象类。这种类只能作为基类,不能创建对象,但可以创建指针和引用。
STL
string
|
输入输出
基于控制台的I/O
cin cout
基于字符串的I/O
istringstream:流的输入
ostringstream:流的输出
stringstream:输入输出
string str = "asdkfjasdf"; stringstream ss(str);//创建字符流对象 ss>>a>>b>>s;//即从ss对象输入其它变量。 ss<<"234"<<"2"<<endl;//输入到字符串 string s2 = ss.str();//转化为字符串 //可以用来做不同类型数据的转换 template<typename out_type, typename in_type> out_type convert(const in_type &t) { stringstream stream; steam<<t; out_type res; stream>>res; return res; } <!--code42-->
//操作样例,从文件输出 |
分文件编写
- h写声明,.cpp写定义,最后新的cpp里引用.h即可
杂乱
头文件
踩坑
pow是浮点计算,貌似算出来的数可能导致不相等,可以加个(int),加(int)会导致不准确,需要自己写函数(不同编译器不一样效果)
cout输出小数不会带后面的0;
当在循环的时候要找到最好的判断方式,在最合适的地方break,不然很容易超时!
cin>>char1不会读入空格,scanf可以
第一个cin.getline之前要放一个getchar
int main()
{
int n;
cin>>n;
getchar();
for(int i = 0; i < n; i++){
int sum = 0;
char str[60];
cin.getline(str, 60);
int len = strlen(str);
for(int j = 0; j < len-1; j++){
if(str[j] == ' ' && str[j+1] != ' ') sum++;
}
if(str[0] == ' ')
{
sum--;
}
cout<<sum+1 <<endl;
}
}
如果定义了拷贝构造函数,记得要把其它属性赋值,不然你传参的时候就错了!!!
析构貌似不用delete[];
考试复习
第一次课
- ‘\xCC’。\x是表示16进制,即CC是十六进制。
- -=返回值是改变后的左侧的数
- Size of char : 1
Size of int : 4
Size of short int : 2
Size of long int(long) : 8
Size of float : 4
Size of double : 8
Size of wchar_t : 4 - 1.0小数默认是double,如果是1.0f则是float
;
可以是一句话- ‘0’——48,‘A’——65,’a’——97;
- 取余
%
必须是整型
第二次课
注意if后面不加{}的话,只能读一句。
int main() { int a = 1, b = 2, c = 3; if(a < b) if(b > c) cout<<3; else cout<<4; //最终输出4,不加括号时,else只能跟最近的if } <!--code45-->
函数原型可以只声明变量类型,而不声明变量名称,且不需要
{}
;函数都要通过main来调用,main是入口,所以函数不能单独运行,但是可以单独编译
第八次课
- 对于
double a[3][5]
,a[1][5]
是不对的! char a[3] = {'a','b','c'}
不可以用strlen(),因为没有结束符’\0’。- 对于c_string的字符数组,注意最后的结束符,另外
\\
是一个字符。
第九次课
- 不可以用1,’a’这样的常量给指针赋值
int a[] = {1,2,3}
a是不可以变的!但是如果是形参,是可以变的!
第十一次课
- 函数模板显示实例化与隐式实例化的条件,如果有普通类型,如int,则必须显示
第十二次课
typedef struct ST |
第十三次课
常成员函数不可以修改属性值,但可以访问。常成员函数不可以调用非常成员函数。
常成员函数可以访问类中的所有数据成员,但不能修改。注意常数据成员要被初始化,(可以在构造函数初始化0
静态函数可以访问静态变量,但不能访问其它变量。普通函数可以访问静态变量。
注意静态数据成员的初始化
普通成员函数不能
:x(a),y(b)
;常对象只能调用常成员函数
拷贝构造函数的参数
const point &p
友元函数可以把原型和定义都写在类里,但是即便写在类里,也不是成员函数。
friend
只能用在类里。当涉及到友元成员函数的时候,因为会涉及到相互依赖的问题,所以只写函数原型,函数定义要放在后面。
%
,必须是int
switch
注意后面是否有break!
第十四次课
- 是double后面要有个小数点么
- 函数默认返回值是int
- 赋值表达式
t=1
的值是t