内容谈不上深刻请高手绕道。
自以为c++学得不差,可是一到笔试立马现原形了。。。果断深刻反省啊!!重新阅读谭浩强的c++和林锐博士<高质量C++/C编程指南>对后者只恨相见晚。。。本文是我自己在阅读时的一些笔记,其中很多表述仅帮助理解不能认真,难免有错误请大家指正。
输入输出
putchar('a') 输出一个字符
a=getchar() 读取一个字符
scanf(格式控制,输出表列)
int a;float b;char c;
sacnf("%d %f %c",&a,&b,&c);
printf(格式控制,输出表列)
控制
switch(grade)
{ case 'A': cout<<"85~100\n";break;
case 'B': cout<<"70~84\n";break;
default: cout<<"error\n";break;
}
do{
...
}
while();
函数
函数中形参是实参的副本
内部函数
static int fun()
外部函数
extern int fun()
声明成外部函数(默认)
在要用的地方声明extern int fun()
函数的参数是数组
函数里面的对数组的修改反应到原值上,也就是说是引用方式实现,因此
void Test(char str[100]){
cout<<sizeof(str);//得到指针大小32位4,64位8
int a[10];
sizeof(a)//虽然a也是指针但这里是数组的大小
}
变量
先看一下内存中的供用户使用的存储空间的情况。 这个存储空间可以分为 三部分,即:
- 程序区
- 静态存储区
- 动态存储区
C++中变量存储类别(storage class) 的属性。 存储类别指的是数据在内存中存储的方法。 存储方法分为静态存储和动态存储两大类。 具体包含4种:自动的(auto)、 静态的(static)、 寄存器的(register) 和外部的(extern)。 根据变量的存储类别,可以知道变量的作用域和存储期
float double
注意到浮点数在其中有舍入误差所以在比较浮点数时不能用等于
const float epsinon=0.000001;
bool check(float x){
if((x>=-epsinon)&&x<=epsinon){
return true;
}
return false;
}
静态局部变量
例如在函数中static int c=3;
则
- 在程序生命期内该内存不释放
- 该句只执行一次
- 对其他函数不可见
extern 声明外部变量
全局变量(外部变量)是在函数的外部定义的,它的作用域为从变量的定义处开始,到本程序文件的末尾。 编译时将全局变量分配在静态存储区。有时需要用 extern 来声明全局变量,以扩展全局变量的作用域。
1.在一个文件内声明全局变量
void main(){
extern int a,b;
//use a,b.......//
}
int a=1,b=1;
//.....//
2.在多文件的程序中声明外部变量
这样使得两个文件共用一个变量
file1.cpp file2.cpp
extern int a,b; int a=3,b=4;
int main( ){
//use a,b...//
}
静态外部变量
对上面的file2.cpp 使用static int
则file1即使使用extern
也不能访问file2的变量
和函数用法一样默认定义的都是可以外部调用的,在要调用的地方声明extern关键字
预处理命令
核心是原样代替
- 宏定义
#define PI 3.14
#define S(a,b) a*b
- 文件包含
-
条件编译
#ifdef 标示符 ||也可以用#if 表达式 程序 #else 程序 #endif
指针
指针无处不在,数组就是指针
指针指向了一块内存单元。
数组
数组是一段连续的内存,有长度(应该是系统确定的)
初始化int a[3][4]={{1},{5},{9}};
使用大括号
定义
int a[10];
int *a=new int[10];
int *a=new int(10);//这个是1个int并初始化为10
访问
ina a[10]; []可看做是变址运算符
cout<<a;//得到的是内存地址,a是指针!但是有长度!
sizeof(a)是40
int *p;毫无疑问p是指向第一个元素的
p=%a[0];等价于p=a;
p+1就是a[i]的地址,*(p+1)就是元素a[i],指针法效率更高
*(*(p+2)+3)==a[2][3]
int (*p)[4]表示p指向数组,这里的大小要和被指向的数组一样。sizeof(p)得到的是指针大小
PS:如果定义为char也可以用cout<<p<<endl;验证得到输出的是地址
由此得出数组是特殊的指针,包含了长度,其他的指针不包含长度
字符数组
char c[]="1234";
在c[4]存在\0
作为结束标志
奇葩的char
char的核心在于首地址和结束符\0
char x;//就一个字符,大小1
//下面是指针
char x[]="xxx";//这个x是指针,但是sizeof(x)是数据的长度
char x[10];//char在使用时要指出大小,sizeof(x)为10,
char *x;//可以有指针,大小8/4
char a[]="1";被定义为char a[2]
char a[]={'1','2','\0','3','4'};
cout<<a<<endl;//和数组处理不一样啊,这里很正常地输出了12
char a[]="123\0123";
cout<<a;//输出 123换行3 此时char a[6]元素为1,2,3,\012,3,\0 由此可看出转义了三位
再测试,在\0后加空格则打印为123。由此可得遇到字符'\0'输出结束。转义符最多可转三位。
x=new char(100);//动态空间用x指向就是个数组
用x[i]访问每个元素
i=3;
*i_p=3
表示i_pointer指向的变量
int * p=&i
p指向i,*p是i,*p是p所指向的单元
函数与指针
指向函数的指针int (*p)(int,int)
*p定义为指针,指针指向函数
p=max;使用方法
p(a,b);
指针函数就是返回指针的函数int *a(int x,int y)
a先与()结合成函数
函数传入的参数是采用副本方式
void getMe(char *p){//这里的P是副本虽然指向原来位置
cout<<p;
p=NULL;
}
main
char *p="123";
getMe(p);
cout<<p;//还是123
cout<<*p;//1 注意首地址
指针的指针
char **p;
char *name[]={"basic","c++"};
p=name+1;
cout<<*p;//c++
cout<<**p;//c
同一数组中,两个指针可以相减得到之间元素个数,指针可比大小后面的大
引用
引用是声明别名,声明完后就不能改了。引用能简化工作
void main(){ void main(){
void swap(int *,int *); void swap(int &,int &);
swap(&a,&b); swap(a,b);
} }
void swap(int *a,int *b){} void swap(int &a,int &b){}
结构体
struct Stu{
int num;
//....//成员列表
}stu1,stu2;//结构体变量
Stu *p=stu1;
(*p)=stu1
三种访问方式
stu1.num
(*p).num
p->num
结构体变量做函数参数和普通int用法都一样参见上面的函数部分。
共用体
共用体就是一个类型的集合,一次只能使用一个类型(一个成员),所占长度是里面MAX(成员长度)
举例:学生和老师,学生显示班级,老师显示职务,在表中同一字段一个int一个string
union data{//名称data可省略
int i;
char ch;
double d;
}a,b,c;
枚举
enum aenum{a=2,b=1,c,d,e}w;//默认序号为前面+1,序号可以重复
在程序中能直接访问a,b,c,d
cout<<c;//c=2
switch (w) {
case a:
cout<<"a";
break;
case b:
cout<<"b";
break;
//这里不能有c,因为识别成了2,重复
w
只能是5个中的一个不能对w
直接赋数字,w=aenum(2)
或者w=c
动态分配内存
分配连续存储空间,返回指针,判断是否NULL
int* p=new int(10);//*(p+i)这样的方式访问,
new char(3.14);//基本类型的
delete p;//普通
new char[10];//数组类型
delete [] p;//对数组
typedef声明类型
typedef int INTEGER
声明结构体
typedef struct name{//name已经没意义了不用写
int xx;
}data;//data是新的类型名,不是结构体的变量
data xxx;
OOP
封装:接口隐藏实现;继承:扩展旧接口;多态:相同接口的不同表现形态;
模板
函数模板
template<class T1,typename T2,typename T3> //模板声明,可多参。class和typename是类型名可以互换
T max(T1 a ,T2 b,T3 c){ //定义一个通用函数,用 T 作虚拟的类型名
...}
int main( )
{
//...//
i=max(i1,i2,i3); //调用模板函数,ix的类型映射到Ti
//...//
}
类模板
template<class type1,class type2>
class a{
public:
type1 max(type2 aa){
return aa;
}
type1 min(type2 bb);
};
//类外的定义
template <class type1,class type2>
type1 a<type1,type2>::min(type2 aa) {
return aa;
}
//定义对象
a<int,float>obj;
函数重载
参数的个数和类型可以都不同。 但不能只有函数的类型不同而参数的个数和类型相同。
运算符重载
实质是函数的重载
class complex{
complex operator+(complex &2);
};
complex complex::operator+(complex &2){
return Complex(real+c2.real, imag+c2.imag);
//或者如下
complex c;
c.real=real+c2.real;
c.imag=imag+c2.imag;
return c;
///
}
实际执行对象相加时编译器解释为
c1.operator+(c2)
可以把函数重载为非成员函数
friend Complex operator + (Complex &c1,Complex &c2);//要增加参数
解释为operator+(c1,c2)
类
构造函数
基本原理:注意到构造函数没有返回,意味着调用时使用this指针指向这个变量
class counter
{
public:
counter()
{
m_value =0;
}
private:
int m_value;
};
对非内置类型成员变量,为了避免两次构造,尽量使用列表初始化
类类型的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,调用构造函数,在进入函数体之后,进行的是对已经构造好的类对象的赋值,又调用个拷贝赋值操作符才能完成(如果并未提供,则使用编译器提供的默认按成员赋值行为)
但有的时候必须用带有初始化列表的构造函数:
- 成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
- const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。
但是初始化表不能初始化静态成员,静态成员只能在类外初始化
class CExample {
public:
int a;
float b;
//构造函数初始化列表
CExample(): a(0),b(8.8)
{}
//构造函数内部赋值
CExample()
{
a=0;
b=8.8;
}
};
构造函数可以是虚函数吗?
不可以,虚函数的执行依赖虚函数表,虚函数表在构造函数中进行初始化,即初始化虚指针,让他指向正确的虚函数表。构造函数,在构造对象期间,虚函数表尚未初始化,无法进行。
构造函数可以是内联函数吗?
可以,但是一般不要使用内联,因为构造函数很多情况下会有隐含动作(比如调用基类成员对象构造函数等)。
构造函数失败如何处理?
构造函数没有返回值,因此无法通过返回错误码来判断构造函数,最好办法应该是抛出一个异常。另外由于构造函数抛出异常时,析构函数就不会执行了,故同时还需要对已经执行的动作(如分配了内存、打开了文件、锁定了信号量等等)进行清理,将这些资源释放掉。
本文原创欢迎大家拍砖,转载请注明O(∩_∩)O谢谢。