C中头文件在C++中
stdio.h --><cstdio>
stdlib.h --><cstdlib>
string.h --><cstring>
ctype.h --><cctype>
time.h --><ctime>
...
bool类型
为假: 0, '\0', NULL, false
其他情况都为真
哑元参数
void fd(int) {}
考虑向前兼容性问题,也有特殊用法,如区分同名函数(前++,后++)
C++参数传递
1,值传递
2,地址传递(指针)
3,引用传递
传参方式的区别:
fa(int x);
fb(int& x);
fc(const int& r);
以下传递参数的方式有的合法,有的非法
int a = 100;
const int& b = 200;
fa(a);//OK
fa(b);//OK
fa(300);//OK
fb(a);//OK
fb(b);//ERROR
fb(300);//ERROR
fc(a);//OK
fc(b);//OK
fc(300);//OK
overload 重载 过载
override 覆盖 重写
函数的重载:
函数名相同,参数列表不同,返回值随意
参数列表不同:
1,类型不同
2,顺序不同
3,个数不同
参数名不同不能认为是参数列表不同
假如以下函数是由其他语言如Java调用,则加入以下语句,意思以C语言方式编译
extern "C"
命名空间目的:
1,按模块,按子系统,按某次制定的逻辑进行分类管理
2,命名冲突
使用方式:
using 命名空间::对象(函数,类,对象...);
using namespace 命名空间;
struct
成员变量,成员函数
union
匿名联合的使用
enum
C++函数
1,没有隐式声明,函数必需先声明或定义后调用
2,返回类型必需明确,没有默认
3,参数严格匹配,空参和void形参是一样的
4,支持哑元
5,函数可以重载
同一访问范围内,函数名相同,参数列表不同
6,参数的默认值
7,支持内联函数
new/delete
类型名*p = new 类型名
delete p;
类型名*p = new 类型名[n];
delete[] p;
以上方式都不会做初始化
类型名*p = new 类型名(初始化值);
引用:
一个变量的别名,在使用时相当于那个变量本身
在不同的作用域范围内用引用访问同一个资源
成员指针:
struct Date{
int y;
int m;
int d;
void show(){
cout<< "Date show" << endl;
}
};
Date d ={2011, 8, 22};
Date da[3]= {{2011, 8, 22}, {2010, 8, 8}, {2012, 12, 12}};
int Date::*mp;//定义成员数据指针
mp =&Date::y;
cout<< d.*mp << endl;
void(Date::* fmp)() = &Date::show;//定义成员函数指针
(d.*fmp)();//调用成员函数
(da->*fmp)();//==*da.*fmp
面向对象编程:
对象: 对事物的一个统称,内存中的一片空间,
保存一些有意义的数据,实际上就是我们所说的变量
拿解决现实中的问题的方式来解决计算机问题,程序中存在大量的对象,对象之间各司其职,个尽其能,不同的对象做不同的事情,对象之间靠发送消息进行互相合作,完成一个系统的运行
对象:对象有什么,对象能干什么
对象属性,对象方法,功能
成员变量,成员函数
对象是客观的存在
类:类型 : 主观的认识,构造对象的模型
构造函数:
构造函数的声明和定义不能同时有默认值
但是一般写在声明部分,因为,若只写定义部分,在当其他导入此头文件时,会无法识别默认形参
对象的构造过程:
1,分配内存空间
2,构造成员变量(成员变量可能是对象)
3,调用构造函数
调用构造函数是构造一个对象的最后一步
成员指针:
结构:类成员的相对地址
class A{
int x;
int y;
void f(int);
};
int A::* mp = &A::x;//相对地址
成员类型 类名::* p;//保存相对地址的成员指针
对象.*成员指针 : 通过成员指针访问该对象的成员
对象.成员名
A a;
a.x;//对象.成员名
a.*mp//对象.*成员名
void (A::*pf)(int) = &A::f;
(a.*pf)(10);
A* p = new A;
(p->*pf)(10);
p->*mp;//*p->x;
构造函数:
函数名和类名相同,没有返回值,连void都没有
在构造对象时调用,也只有要构造对象的时候调用
构造函数的主要目的是初始化构造的对象,除了初始化工作之外,可以在构造函数中做任何事
初始化一般指的是成员变量赋值的初始值,或准备网络连接,或准备数据库连接,或初始一些程序运行所需要的环境
构造函数也是函数,具备函数的特征,可以重载,可以给参数默认值
构造函数可以有初始化列表,初始化列表是在创建成员变量时直接初始化的
对象的构造函数
写一个类,可以把类分解成两部分,头文件和实现
1,函数的参数默认值必需在声明部分
2,构造函数的初始化列表必需在实现部分
3,类范围以外的所有成员,函数名前加 类名::
4,如果一个成员函数或构造函数的函数体是空的或只有一两条语句,可以考虑不要拆开,而直接在头文件中实现,这个函数将被处理为内联函数
const限定符:
在非成员函数中,不能加const来声明为只读函数,因为对函数而言,CV限定符是限定成员变量不可修改,非成员函数没有成员变量,所以不能加CV限定符
malloc和new区别:
1,malloc不调用构造函数,使用free释放时也不调用析构函数
2,malloc之后需要强转成对应类型的指针
在构造对象时,如果申请了堆空间,网络连接,数据库连接,打开文件...在释放对象之前就要释放这些资源,析构函数就提供了一个释放这些空间的最佳场所
拷贝构造函数
当同类型对象构造性对象时
何时调用拷贝构造
A a;
A b = a;//调用
f(A a);
void fa(A a);//传入参数时调用拷贝构造
A fb();//返回时调用拷贝构造
A& fc(A& a) {
return a;
}
fc(a);//不会
A& b = fc(a);
A x = fb(a);
拷贝构造函数的调用时机,产生新对象时.如果是用同类型的另一个对象构造,就会调用
只要有新对象产生,一定会调用构造函数,如果构造这个对象时用同类型的另一个对象生成,就会调用拷贝构造,否则就可能是调用空参构造函数或有参的构造函数
当类中存在指针时,指针所指向的内容必需在拷贝构造中处理
析构函数
只有一个,对象释放前调用,构造函数中主要做的是释放一些不能自动释放的资源,如堆空间,网络连接,数据库连接,文件...一般情况下,这些资源是对象构造时申请的
this指针
1,发生成员变量和局部变量命名冲突时,用this指针
2,成员函数返回当前对象时,一般返回*this,这种情况下的返回类型一般为当前类型的引用
3,有时在成员函数中调用另一个函数需要传当前类型的参数时,可以将this作为参数传递
4,凡是在成员函数中需要当前对象的地方,都可以用this,this是指向当前对象的指针,*this就是当前对象
5,所谓当前对象,就是谁调用了这个成员函数,谁就是当前对象
const在类中使用
class A {
int x;
int y;
public:
void read() const {}//只读成员函数
void write() {}//普通成员函数
};
对于const对象,只能调用只读的成员函数
对于非const对象,可以调用只读成员函数,也可以调用非只读函数,如果两个同名的函数,一个只读,一个非只读,优先调用非只读函数
只读函数仅是成员函数,在此函数中,不能修改成员变量的值,只能读取他们的值
mutable成员变量可以被只读的成员函数修改
静态成员:
包括静态成员变量和静态成员函数
静态成员属于整个类,和具体的对象无关
运算符重载
在C++中,运算符其实就是函数,使用运算符进行计算时,其实相当于在调用函数进行运算,我们把运算符函数之间形成的重载关系叫做运算符重载
cout << x << endl;//实质如下
cout.operator<<(x).operator<<(endl);
赋值运算符通用写法:
1,如果是自赋值,返回当前对象
2,释放原有空间
3,申请新的空间
4,复制数据
5,返回当前对象
运算符重载:
C++所有运算符进行都是调用运算符函数,如果你需要的运算符函数不存在,自己写一个,这个函数一般都和其他的运算符函数形成重载关系
成员函数和友元函数区别
1,成员函数类的对象,友元函数不是类的成员,是全局函数
2,成员函数有this指针,可以访问当前对象,友元没有this指针,不存在当前对象
3,成员函数必需通过对象.的方式访问,友元只能直接调用
4,双目运算符建议用友元函数
单目运算符
~ ! - ++ --
建议用成员函数
前++
A&operator() {
++..
return*this;
}
返回的是对象本身
后++
const Aoperator(int) {
A old =*this;
++..
return old;
}
返回的是临时变量
编译器默认写的函数:
class A {
A() {}
A(constA&) {}
~A() {}
A&operator=(const A&);
constA*operator&() const;//为const对象
A*operator&(); //为非const对象
}
定义下标操作符时,一般需要定义两个版本:
1)非const成员并返回引用,用于非const对象
int&operator[] (const size_t);
2)const成员并返回const引用,用于const对象
constint&operator[] (const size_t) const;
->运算符
当我们将指针封装成对象,或把一个对象当指针来用,就需要重载->运算符
面向对象三大特性:
封装, 继承, 多态
封装:
给外界公开访问接口,具体实现隐藏
提高程序的可维护性和可扩展性
继承:
两个类之间的关系,当一个类继承另一个类时,这个类就拥有了另一个类的所有成员
分为: 多继承,单继承
C++ : 公开继承, 保护继承, 私有继承
class B : public A {}
class B : protected A {}
class B : private A {}
class B : A {}//private
C++类中成员有以下几种:
private, protected, public
C++继承关系的构造过程:
1,分配内存空间
2,逐级的构造父类对象(嵌套重复2,3,4)
1)构造成员变量
2)调用构造函数
3,构造成员变量
3,调用构造函数
子类不会继承父类的构造函数,析构函数,和运算符函数,但是依然可以调用
名字隐藏:
如果子类出现一个和父类中函数名相同的函数,
就会把从父类继承下来的函数隐藏起来
名字隐藏只要函数名相同就会发生,与参数和返回值无关
两个类之间的关系
继承, 组合, 聚合, 关联 ...
is a 是一个 Dog is aanimal 继承
has a 有一个 Computer has a CPU 组合/聚合
为什么继承:
1,代码重用
继承,组合/聚合
慎用继承,继承的问题:1)提高系统复杂度
2)破坏封装
2,制定规范
3,为了多态
虚函数:
虚函数属于覆盖override
1,函数名相同,参数列表相同,返回值相同
2,访问权限不能更小
3,抛出异常不能更多
面向对象的三大特征:
封装, 继承, 多态
封装:隐藏,具体实现隐藏,对外界只公开访问接口,提高软件的维护性和扩展性
继承:代码重用,制定规范,提供多态
多态:变量的多态
函数的重载
类的多态:
当用父类的指针指向于子类的对象,或父类的引用引用到子类对象,通过父类指针和引用调用函数时,函数的行为取决于指向的(引用到的)对象,这种情况叫做多态
C++多态的前提:
1,函数必需是虚函数
2,父子类之间的虚函数一定是覆盖的
1,只存在于虚函数中
2,函数名相同,参数相同,返回值相同
3,子类函数中的异常不能比父类函数中的多
4,子类中函数的访问权限与父类相同或更多
纯虚函数
如果一个类存在纯虚函数,那么这个类为抽象类
不能用抽象类构造对象
如果一个类中的所有函数均为纯虚函数,那么这个类可以叫做接口类
面向接口编程:
类与类之间没有依赖,类只依赖于接口,而不依赖具体的实现
虚函数
根据虚表查找所调用的函数,有虚函数与无虚函数的类相比要多4个字节,这4个字节就是存放指向虚表的指针,如果改变此指针也就会改变其运行时指向的函数
class A {
public:
virtual void show() {}
};
class B :public A {};
class C : public B {};
class D : public B {};
dynamic_cast只能对含有虚函数的类进行转换
typeid的主要作用是在多态情况下,获取一个父类指针或引用指向的对象的具体类型信息
iostream:
C++标准要求,cin,cout等输入输出对象在正常情况下返回逻辑真,出现问题时返回逻辑假,当它们出现问题时就拒绝工作,此时要让它们恢复成正常状态才可以继续工作
cin.clear();//清除错误状态,不会清除缓冲区
cin.ignore(100,'\n');//清除缓冲区
多态:
C++中的多态是可选择的
多态的主要目的:更加通用的程序,更加低的耦合,维护性,扩展性的提高
有虚函数的类才会有多态
父类的指针,指向之类的对象,父类的引用引用到子类的对象
函数的参数是指针或引用的时候,函数就可以接收任何子类的对象
父类的指针 P = 子类的对象;
p->函数;//多态性
p的编译期类型是父类型,所调用的函数必需父类有
p的运行期类型是子类型,真正调用的函数是子类的函数
前提是函数是虚函数,运行时是通过对象的虚表指针找到虚函数表来调用虚函数的
抽象类:
比较关键的类,经常当接口来用,用来搭建系统的架构,隔离类与类之间,层与层之间,模块与模块之间,降低它们的耦合
有纯虚函数的类叫做抽象类
不能构造对象,但可以声明引用变量,指针变量...
为了让别人继承它,指定规范让子类遵守,抽象类可以强制的让子类实现指定的函数
抽象类是很多设计模式实现时必需使用的
dynamic_cast
父子类的指针之间或引用之间的强制类型转换,使用的前提是父类有虚函数
假如B是A的子类
A* pa = new B;
B* pb = dynamic_cast<B*>pa;
在运行时,如果pa指向的对象是B类对象,返回对象的地址,意味着转换成功,如果返回为NULL,就说明pa指向的对象不是B类型,不能转换
如果存在如下继承关系,A<--B<--C<--D,那么D类型的对象也是C,B,A类型
typeid
运行时获取对象的类型信息
返回一个type_info结构变量的引用
#include <typeinfo>
const type_info&
.name()返回类型的名称
== != 比较两个类型是不是同一个类型
使用前提是类型必需要有虚函数,判断一个对象属于什么类型
参数可以是对象,也可以是类名
A* p = new B;
typeid(*p) == typeid(B);
虚析构函数
有虚函数的类,析构函数必需写虚函数
A* p = new B;
delete p;
为什么把析构函数写成虚函数:
当父类的指针指向堆中的子类对象时,通过父类的指针释放子类对象,如果析构函数不是虚函数,那么编译器在编译时会静态绑定调用父类的析构函数,为了保证子类的析构函数正确调用,需要在运行时通过对象的虚表指针指向的虚函数表来决定调用哪个析构函数,所以析构函数必需写成虚函数
对文件读写操作
.#include <fstream>
ifstream ofstream fstream
格式化的,和cout,cin的用法一样
非格式,调用一些函数
C++中的输入输出对象,当正常工作时,其逻辑值为true,出现错误时逻辑值为
false,拒绝工作
clear()
ignore();
getline();
异常:
抛出异常:
不声明,相当于声明抛出任何异常
throw(),声明不抛出异常
void fa() throw(){}
throw(异常类型, ...);
处理异常
try {
} catch (异常类型1&) {
} catch (异常类型2&) {
}