编辑 -> 编译(.cpp) -> 连接(.obj) -> 运行(.exe) -> 分析结果
! > 算术运算符 > 关系运算符 > &&和|| > 赋值运算符
switch(表达式)
{
case 常量表达式1: 语句1; break;
case 常量表达式2: 语句2; break;
...
default: 语句; break;
}
while(表达式1)
{
if()
continue; //结束本次循环,继续下次循环
if()
break; //结束全部循环
}
C++在程序进行编译时,以程序文件模块为编译单位
在编译时将调用函数的代码直接嵌入到主调函数中,而不是将流程转出去
inline int max(int, int, int);
inline int max(int a, int b, int c)
{
...
}
重载函数的参数格式、参数类型、参数顺序三者中必须至少有一种不同,返回值类型可同可不同
函数模板只适用于函数体相同、函数的参数相同而类型不同的情况
如果参数的个数不同,则不能用函数模板。
template
T max(T a, T b, T c)
{
...
}
int f(int a)
{
auto int b, c = 3; //自动变量,默认为auto,可省略
}
int f(int a)
{
static int b = 0; //静态局部变量
}
int f(int a)
{
register int i=1; //寄存器变量
}
1.在一个文件内声明全局变量
int main()
{
extern int a, b; //全局变量的提前引用声明
cout<
2.在多文件的程序中声明外部变量
file1.cpp
int a=3, b=4;
...
file2.cpp
extern int a, b; //在编译连接成一个程序后,将file1.cpp中的a、b作用域扩展到file2.cpp
int main()
{
cout<
file1.cpp
static int a = 3; //静态外部变量,只限于本文件中用
file2.cpp
extern int a; //引用不到file2.cpp中的a,因为是静态外部变量
int main()
{
cout<
static int func(int a, int b)
{
...
}
int max(int x, int y)
{
...
}
file2.cpp
int main()
{
extern int max(int, int); //声明要调用其他文件中的max函数
max(1, 2);
}
\0
(ASCII码为0)int a[10]; //不允许对数组的大小作动态定义
const int n = 5;
int a[n]; //合法,因为n是常量
int a[3][4];
int a[][4] = {1, 2, 3, 4, 5, 6}//第2维的长度不能省略
char a[10] = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'};
char c[] = "I am happy"; //正确
c = a; //错误
c = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'}; //错误
c[0] = 'I'; //正确
char str[20];
cin>>str; //输入字符串
cout<
#include
或
#include
strcat
:字符串连接函数strcat()
strcpy
:字符串复制函数(覆盖)char str1[10], str2[] = "China";
strcpy(str1, str2); //第1个实参必须是数组名,第2个实参可以是数组名,也可以是字符串常量
strcpy(str1, "China");
strcpy(str1, str2, 2); //将str2中前面2个字符复制到str1中去,然后加上'\0'
strcmp
:字符串比较函数strcmp(str1, str2); //正确
strcmp("China", "Korea"); //正确
strcmp(str1, "Beijing"); //正确
串1 == 串2,返回0
串1 > 串2,返回正数
串1 < 串2,返回负数
strlen
:字符串长度函数char str[10] = "China";
cout<
string不是C++本身的基本类型,而是C++标准库中声明的一个字符串类
#include
string s1, s2;
s1 = "Hello";
s2[0] = 'a'; //合法修改第一个字符
cin>>s1;
cout<
string s1, s2;
s1 = "Hello";
s2 = s1; //赋值运算
s2 = s1 + s2; //连接
if(s1 > s2) ...; //比较运算符
string name[3] = {"Zhang", "Li", "Sun"};
&
*
自右向左结合
int a = 10;
int *pointer_1, *pointer_2;
pointer_1=&a;
pointer_2 = &*pointer_1; //从右向左执行,将a的地址赋给pointer_2
调用函数时不会改变实参指针变量的值(地址),但可改变实参指针变量所指向的值
int a = 10
int *p = a;
fun(p);
int fun(int *p){} //不改变p的值,但可改变a的值
启示指向的是数组的第一个元素
int a[10];
int *p;
p=&a[0]; //等价于
p=a;
p+1 //表示数组的下一个元素
void select_sort(int array[], int n) //等价于
void select_sort(int *array, int n)
int a=1, b=1;
int max(int x, int y);
int (*p)(int, int); // 是变量,可以指向函数
p=max
p(a, b) //用函数指针调用 等价于
max(a, b)
int *p(int x, int y) {} // 是函数,返回值为int型指针
int *p[4]; //每个元素都是指针类型
相当于:
int *p0; int *p1; int *p2; int *p3;
例:
char *name[] = {"peter", "jack", "smith"}; //指针数组
[]
优先级比*
高
注意:
int (*p)[4]; //指向一维数组的指针变量 用来指向二维数组
#include
using namespace std;
int main()
{
int w[3][4] = {{1,2,3, 11}, {4,5,6, 12}, {7,8,9, 13}};
int (*p)[4] = w; //指向一维数组的指针变量
for(int i=0; i<3; i++)
{
for(int j=0; j<4; j++)
cout<
const int *p = &a; // 不允许通过p修改a的值
*p = 10; //非法
p = &b; //合法
int * const p = &a; //不允许修改p的指向,但允许修改a的值;定义时要初始化
*p = 10; //合法
p = &b; //非法
1.常变量只能由 [指向常变量的指针变量] 指向它,不能用 [一般指针]
2.[指向常变量的指针变量] 也可以指向[一般变量],那么用该指针变量访问时有常变量的特征
3.[一般指针]只能指向[一般变量]
const int * const p = &a;
*p = 10; //非法
p = &b; //非法
可以把
非void型
的指针赋给void型
指针变量,但不能把void型
指针直接赋给非void型
,必须先进行强制转换
int a = 3;
int *p1 = &a;
char *p2 = "new";
void *p3;
p3 = (void *)p1; //将p1的值转换为void *类型,赋给p3
cout<<*p3<
int main()
{
int a = 3;
int *p1 = &a;
void *p2 = p1;
cout<
定义 | 类型 | 含义 |
---|---|---|
int *p; | int * | p为指针变量,指向int型数据 |
int *p[4]; | int *[4] | p为指针数组,指向4个元素都是int型指针的数组 |
int (*p)[4]; | int (*)[4] | p为指针变量,指向4个int数据的一维数组 |
int *p (); | int * () | p为正常函数,只是函数返回值为int型指针 |
int (*p)(); | int (*)() | p为指针变量,指向函数 |
int **p; | int ** | p为指针变量,指向一个指针(该指针指向int型数据) |
int * const p | int * const | p是指针变量,常指针,指向不能变 |
const int *p int const * p |
const int * | p是指针变量,指向常量,不能通过p修改值 |
const int * const p | const int * const | p是指针变量,指向不能变且也不能通过p修改值 |
void *p | void * | p是指针变量,基类型为空(void),不指向具体对象 |
变量的“别名”
int a;
int &b = a; /* 引用只有声明,没有定义 */
int &b; /* 非法!引用声明的同时必须初始化 */
int a1, a2;
int &b = a1;
int &b = a2; /* 非法!引用声明后不能变更 */
int a[5];
int &b[5] = a; //非法!不能建立引用数组
int &c[5] = a[0]; //非法!不能作为数组元素的别名
int a;
int &b = a;
int *p = b; //非法!不能建立指向引用的指针
int a;
int &b = a;
int *p = &b; //合法,&b是a的地址
void swap(int a, int b) {
...
}
void main(){
int i=1, j=2;
swap(i, j);
}
void swap(int *p1, int *p2){
...
}
void main(){
int i=1, j=2;
swap(&i, &j);
}
void swap(int &a, int &b){
...
}
void main(){
int i=1, j=2;
swap(i, j);
}
struct
)struct Student
{
int num;
char name[20];
char sex;
} student1 = {1001, "Zhangsan", 'M'}; //最后一个分号不能少!!!
Student student2 = {1002, "Wangwu", 'F'};
student1 = student2; //相同结构体类型变量可相互赋值
student.num = 10010; //"."是成员运算符
Student stu[3] = {1001, "Jack", 'M', 1002, "Peter", 'F', 1003, "mali", 'M'}; //结构体数组
Student *p = &student1; //结构体指针
等价
student1.num;
(*p).num; // "." > "*"
p->num;
// "->" > "++"
p->num++; //得到num后,使用完加1
++p->num; //等到num,加1后使用
见考试大纲
new
和delete
int *p1 = new int(100); //整型数
int *p2 = new int[10]; //整型数组
delete p1; //释放变量
delete [] p2; //释放数组
enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat}; //tue为2,wed为3...往后加1
Weekday workday;
workday = tue; //正确
workday = 2; //错误
workday = (Weekday)2; //C语言风格强制类型转化
workday = Weekday(2); //C++风格强制类型转化
typedef int INTEGER;
INTERGER I;
typedef int NUM[100]; //声明NUM为整型数组类型,包含100元素
NUM n;
typedef char *STRING; //声明STRING为char *类型,即字符指针类型
STRING p;
typedef int (*POINTER)(); //声明POINTER为指向函数的指针,函数返回整数值
POINTER P;
typedef struct
{
int month;
int day;
int year;
} DATE; //声明DATE为结构体类型
DATE birthday;
格式:
class Student
{
private:
int num;
int score;
char name[20];
public:
Student(){} //无参构造函数
Student(int n) //一个参数的构造函数
{
num = n;
score = 0;
strcpy(name, "123");
}
Student(char nam[], int n=0, int score=0) //默认参数的构造函数
{
num = n;
score = n;
strcpy(name, nam);
}
Student(int n, int s, char nam[]):num(n), score(s){strcpy(name, nam);} //参数初始化列表
}
注意项:
Student s; //正确
Student s(); //错误!不能显式调用
Student(int = 1001, int = 20); //省略形参名声明合法
//下面两同时存在是错误的!二义性
Stduent();
Student(int num=10, int score=0);
Student s; //产生二义性
//产生二义性
Box();
Box(int, int);
Box(int =10, int =10, int =10);
4种情况下执行析构函数:
析构函数不是删除对象,只是在撤销对象占用的内存之前完成一些清理工作
先构造的后析构,后构造的先析构
Time *pt;
Time t1;
pt = &ti;
//等价于
(*pt).hour;
pt->hour; // "->"是指针专用的
成员函数指针变量:
void (Time::*p2)();
p2=&Time::get_time;
要求三个匹配
每个成员函数都包含一个特殊的指针,指向本类对象的指针,他的值是当前被调用的成员函数所在的对象的起始地址。
//等价于
(*this).height
this->height
height
优先级:"." > “*”
常对象
//等价
Time const t1(12, 34, 46);
const Time t1(12, 34, 46);
mutable int count; //mutable 可变数据成员
常数据成员(访问不限,不可改值)
class Time
{
const int hour;
}
常成员函数(访问不限,不可改值)
class Time
{
void get_time() const;
}
Time t1(19 ,12, 15);
Time * const p1;
p1 = &t1;
void fun(Time * const t);
同 指向常变量的指针变量
void fun(const Time *t);
void fun(const Time &t); //t所指向的变量的值不能变
Time *t = new Time;
delete t; //在释放内存空间前,自动调用析构函数
Time t1, t2;
...
t1 = t2; //同属一类的两个对象可相互赋值
Box::Box(const Box&b) //复制构造函数 注意参数是引用!!
{
height = b.height;
width =b.width;
}
...
Box box2(box1); //对象复制
对象复制 | 对象赋值 |
---|---|
从无到有建立新对象 | 对已存在的对象进行赋值 |
对象复制的使用场景:
Box box1(12, 15, 16);
Box box2(box1); //建立新对象box2
void fun(Box b) //函数调用时的虚实结合
{
...
}
Box f()
{
Box box(12, 2, 4);
return box; //赋值给一个“临时对象”返回给函数调用处
}
int main()
{
Box b = f();
return 0;
}
class Box
{
public:
static int height; //静态数据成员
staitc int volume(); //静态成员函数
}
int Box::height = 10; //静态数据成员只能在类体外进行初始化
int Box::volume()
{
return height;
}
int main()
{
Box a;
//静态成员
a.height; //可以,通过对象引用
Box::height; //也可以,通过类名引用
//静态成员函数
a.volume();
Box::volume();
return 0;
}
静态成员
静态成员函数
this
指针对象名.非静态成员函数
1.普通函数作为友元函数
class Time
{
private:
int hour;
public:
friend void display(Time &); //友元函数(不属于该类)
}
void display(Time &t) //普通函数
{
t.hour; //可以通过对象名访问私有成员
}
this
指针2.一个类的成员函数,作为另一个类的友元函数
class Date; //提前引用声明
/* 客人 */
class Time
{
private:
int hour;
public:
void display(Date &); //正常成员函数
}
/* 主人 */
class Date
{
private:
int hour;
public:
friend void Time::display(Date &); //Time类中的display函数作为本类的友元函数
}
void Time::display(Date &d)
{
d.hour; // 别人家的私有数据成员
hour; //自己家的私有数据成员
}
注意项:
对象名.数据成员
//B是A的友元类,B中的所有成员函数都是A的友元函数
class B;
class A
{
public:
friend class B; //B是A的友元类
}
class B
{
...
}
功能相同,只是数据类型不同
template
class Compare
{
public:
Compare(T a, T b) //构造函数
{
x=a;
y=b;
}
T max() //成员函数类内定义
{ return (x>y)?x:y; }
T min();
private:
T x, y;
}
template
T Compare::min() //类外定义
{ return (x>y)?x:y;}
int main()
{
Compare f1(3, 7);
Compare f2(45.2, 30.1);
return 0;
}
三部曲:
template
多个类型参数的情况:
template
class Box
{
...
}
类模板 | 模板类 |
---|---|
重点是模板,产生类的模子 | 重点是类,由模板产生的类 |
int operator+ (int a, int b)
{
return (a+b);
}
. * :: sizeof ?:
第一个参数是this指针隐式调用的访问
class Complex
{
private:
double real;
double imag;
public:
Complex operator+(Complex &c2); //成员函数运算符重载函数
};
Complex Complex::operator+(Complex &c2)
{
Complex c;
c.real = real + c2.real;
c.imag = imag + c2.imag;
return c;
}
没有this指针
class Complex
{
private:
double real;
double imag;
public:
friend Complex operator+(Complex &c1, Complex &c2); //友元函数运算符重载函数
};
Complex operator+(Complex &c1, Complex &c2)
{
Complex c;
c.real = c1.real + c2.real;
c.imag = c1.imag + c2.imag;
return c;
}
C++规定 有一个int型形参的重载函数,是后置自增自减运算函数
class Time
{
private:
int minute;
int sec;
public:
Time operator++(); //++i
Time operator++(int); //i++
};
Time Time::operator++() //++i
{
if(++sec >= 60)
{
sec-=60;
++minute;
}
return *this;
}
Time Time::operator++(int) //i++
{
Time temp(*this); //临时对象 保存自加前的状态
sec++;
if(sec >= 60)
{
sec-=60;
++minute;
}
return temp; //返回的是自加前的对象
}
class Complex
{
private:
double real;
double imag;
public:
friend ostream& operator<< (ostream & , Complex & );
friend istream& operator>> (istream & , Complex & );
};
ostream& operator<<(ostream &output, Complex& c)
{
output<< "(" << c.real << "+" <>(istream& input, Complex& c)
{
cout<<"please input real part and imaginary part of complex number:";
input >> c.real >> c.imag;
return input;
}
只有一个参数,如果有多个参数,他就不是转换构造函数
class Complex
{
private:
double real;
double imag;
public:
Complex(double r)
{
real = r; //将double型r转换为Complex类的对象
imag = 0;
}
}
class Complex
{
private:
double real;
double imag;
public:
operator double()
{
return real;
}
}
若派生类中声明一个与基类成员同名成员,则派生类中的新成员会覆盖基类的同名成员;对于成员函数要考虑函数重载的情况。
基类成员在基类的访问属性 | 继承方式 | 基类成员在派生类的访问属性 |
---|---|---|
public | public | public |
protected | public | protected |
private | public | 不可访问 |
public | protected | protected |
protected | protected | protected |
private | protected | 不可访问 |
public | private | private |
protected | private | private |
private | private | 不可访问 |
派生类中访问属性 | 在派生类中 | 在派生类外部 | 在下一层公用派生类 |
---|---|---|---|
public | 可以 | 可以 | 可以 |
protected | 可以 | 不可以 | 可以 |
private | 可以 | 不可以 | 不可以 |
不可访问 | 不可以 | 不可以 | 不可以 |
派生类的构造函数
Class A //基类
{
private:
int x, y;
public:
A(int a, int b) //基类构造函数
{
x=a;
y=b;
}
};
/* 形式1: */
class B:public A //派生类
{
private:
int z;
public:
B(int a, int b, int c):A(a, b) //派生类构造函数
{
z=c;
}
};
/* 形式2 */
class B:public A //派生类
{
private:
int z;
public:
B(int a, int b, int c); //派生类构造函数声明
};
//类外定义
B::B(int a, int b, int c):A(a, b)
{
z=c;
}
构造函数执行顺序:
派生类只需写其上一层派生类的构造函数,不必每一层都写出来
派生类构造函数特殊形式
析构函数执行顺序:
class D: public A, public B, protected C
{
...
};
多重继承构造函数调用顺序:
多重继承析构函数调用顺序:
与多继承构造函数调用顺序相反
在继承间接共同基类时,只保留一份基类成员
class A //基类
{};
class B : virtual public A //B是A的公有派生类,A是B的虚基类
{};
class C : virtual public A //C是A的公有派生类,A是C的虚基类
{};
虚基类的初始化
class A //基类A
{
A(int i){};
};
class B : virtual public A //A是B的虚基类
{
B(int n): A(n){}
};
class C : virtual public A //A是C的虚基类
{
C(int n):A(n){}
};
class D : public B, public C //正常继承
{
D(int n): A(n), B(n), C(n){} //D中要对所有基类初始化;重要!也就是还要对虚基类A初始化
};
只有公有派生类才是基类的真正子类型,它完整的继承了基类的功能
儿子可以给爸爸赋值,反之不对
A a1; //基类A
B b1; //A的派生类B
a1 = b1; //舍弃派生类自己独有的成员,“大材小用”
A a1;
B b1;
A &r = b1; //指的是b1中基类的那一部分的别名
void fun(A &r){}
fun(b1); //正确 只输出派生类中的基类成员
指向儿子中继承的那一部分成员
A *p1;
B b;
p1 = &b; //指向B中继承A的那部分数据
类的继承派生体现的是 “是”的关系
类的组合(子对象)体现的是“有”的关系
继承时纵向的,组合是横向的
静态多态性 | 动态多态性 |
---|---|
通过函数重载实现,编译时多态 | 通过虚函数实现,运行时多态 |
虚函数 | 函数重载 |
---|---|
函数首部是相同的 | 函数首部是不同的(参数个数或类型) |
同一类族中不同类的对象,对同一函数调用作出不同的响应
#include
using namespace std;
class Student
{
protected:
int num;
public:
Student(int n):num(n){};
virtual void display() //虚函数
{
cout<display();
p=&g; //父类指针指向子类
/*
若display不是虚函数,那么只能调到父类的display方法;
若display在基类中被声明为虚函数,那么可以通过父类指针调到子类的display函数
*/
p->display();
return 0;
}
1001
1001 4500.5
使用虚函数
virtual
vitual
可加可不加未声明为虚析构函数时,new 出来的对象,delete时只会调用基类的析构函数
#include
using namespace std;
class Point
{
public:
Point(){}
virtual ~Point() //虚析构函数
{
cout<<"executing Point destructor"<
executing Circle destructor
executing Point destructor
virtual float area() const = 0;
定义抽象类的目的:用它作为基类去建立派生类
动态关联
静态关联
两个基类
有关头文件
#include
用流对象的成员函数控制
precision(n); //实数精度为n为
width(n); //字段宽度n位
fill(c); //设置填充字符c
设置格式
setf(); //设置格式
unsetf(); //取消设置格式
参数:
iso::left
iso::dec //整数基数是10
iso::oct //整数基数是8
iso::hex //整数基数是16
成员函数width(n)
和setw(n)
只对其后面第1个输出项有效
要重新设置格式setf()
,先取消usetf()
setf()
可以设置多个 setf(ios::internal | ios::showpos)
成员函数put()
专门用于输出单个字符的成员函数put
cout.put('A');
键盘输入完按Enter后输入,跳过空格、Tab键、换行符等空白字符
if(!cin)
cout<<"error";
当遇到输入流中的文件结束符,函数返回EOF,即**-1**
#include
using namespace std;
int main
{
int c;
while( ( c = cin.get() )!=EOF )
cout.put(c);
return 0;
}
cin.get(ch); //从输入流中读取一个字符,赋给ch
/* 等价 */
cin.get(字符数组, 字符个数n, 终止字符); //从输入流中读取n-1字符,赋给字符数组 n-1个有效字符和一个'\0'
cin.getline(字符数组, 字符个数n, 终止字符);
文件结束返回 非零
文件没结束返回 0
#include
using namespace std;
int main()
{
char c;
while( !cin.eof() ) //eof()为假表示未遇到文件结束符
if( (c=cin.get())!=' ' ) //检查读入的字符是否为空格字符
cout.put(c);
return 0;
}
c = cin.peek(); //返回指针当前指向的字符
cin.putback(ch); //将ch插在当前指针之后的位置(注意是插入,不是覆盖)
cin.ignore(n, 终止字符) ; //跳过n个字符或者 终止字符以前的都被忽略
#include
using namespace std;
int main()
{
char ch[20];
cin.ignore(18, 'A');
cin.get(ch, 20, '/');
cout<
abcA123/
123
文件两大类:
根据文件中数据的组织形式分:
I/O功能
1.打开文件
ofstream outfile;
outfile.open("f1.dat", ios::out); //以输出方式打开一个文件
方式 | 作用 |
---|---|
ios::in | 以输入方式打开文件 |
ios::out | 以输出方式打开文件(默认方式),如果已有此名字的文件,则将其原有的内容全部清除 |
ios::app | 以输出方式打开文件,追加写 |
ios::ate | 打开已有文件,文件指针指向文件末尾 |
ios::trunc | 打开文件,若如果文件存在,则删除其中的全部数据;如果不存在,则建立新文件。ios::out方式的默认格式 |
ios::binary | 二进制格式打开文件,若没指定则默认ASCII方式打开 |
ios::nocreate | 打开一个已有文件,如文件不存在,则打开失败 |
ios::noreplace | 如果文件不存在则建立新文件,如果文件存在则操作失败 |
ios::in | ios::out | 以输入输出方式打开文件,可读可写 |
ios::out | ios::binary | 以二进制输出方式打开文件 |
ios::in | ios::binary | 以二进制输入方式打开文件 |
//检测文件打开是否失败
if(outfile.open("f1.dat", ios::app) ==0)
cout<<"open error";
if( !outfile.open("f1.dat", ios::app) )
cout<<"open error";
outfile.close(); //关闭文件
例. 键盘输入10个整数送到数组,在将数组存到磁盘文件
#include
#include
using namespace std;
int main()
{
int a[10];
ofstream outfile("d:\\f1.dat", ios::out); //输出格式打开文件
if( !outfile ) //判断是否打开成功,打开失败时返回0
{
cerr<<"open error!"<>a[i];
outfile<
与ASCII码文件不同
既能做输入又能输出的文件
istream &read(char *buffer, int len); //读
ostream &write(const char *buffer, int len); //写
#include
#include
using namespace std;
struct Student
{
char name[20];
int num;
int age;
char sex;
};
int main()
{
Student stu[2] = {{"Li", 1001, 18, 'f'}, {"Wang", 1002, 17, 'f'}};
//输出到磁盘文件
ofstream outfile("d:\\stu.dat", ios::binary);
if(!outfile)
{
cerr<<"open error"<
1001 Li 18 f
1002 Wang 17 f
infile.seekg(100); //输入文件位置标记向前移动100字节位置
infile.seekg(-50, ios::cur); //输入文件中位置标记从当前位置后移50字节
outfile.seekp(-75, ios::end); //输出文件中位置标记从文件尾后移50字节