笔记随时增改,欢迎大佬指正
类的访问修饰符:
public://公有的,都可见
protected: //受保护的,类内及子类可见
private: //私有的,类内可见
friend: //友元,类内友元声明函数/其他类,之后此 类或此函数对本类可见。
注:使用接口函数可间接使用private部分的成员
构造函数: 类名(参数列表) //给数据成员赋初始值
{ //可通过对象传递数据
… //初始化列表,在参数列表 之后:变量名(初始化值)
}
1、继承的小实例:
class CPeople
{
public:
int hands;
int head;
void speak(){ }
void study(){ }
};
class CXiaoming : public CPeople //子类(派生类)xiaoming继承了父类(基类)CPeople中public的部分成员
{
public:
int age;
char *name;
void speakChinese(){ }
void work() { }
};
class CXiaohua : public CPeople,pubilc CXiaoming //多继承,可继承多个类
{
public:
int age;
char *name;
void alanguage(){ }
};
int main()
{
CXiaoming XM;
XM.hands;//.....
CXiaohua XH;
XH.hands;//......
return 0;
}
2、继承增加了代码的重用性
3、通过派生类对象可调用基类成员,若基类与派生类有同名对象,则需加类名作用域区分。
CSon so;
cout << so.a << endl; //未加类名作用域,就默认调用CSon自己的成员
cout << so.CFather::a << endl;
4、继承限定词(访问修饰符):
public —保持原基类访问修饰符
protected --降低基类public修饰符权限为protected,其他不变
private --降低基类public,protected修饰符权限为private
class CFather : public CSon,protected CSon1,private CSon2
5、继承构造函数调用顺序:先调用父类的构造函数,在调用子类的构造函数;若父类构造函数有参数,需通过子类构造函数传递(可巧妙利用变量关联起来);子类构造函数有参数直接在创建对象时传递;
class CFather //基类
{
public:
CFather(int b)
{
cout << "I am Father " << endl;
}
};
class CSon : public CFather //派生类
{
public:
CSon(int a):CFather(a) //父类构造函数参数传递写法,可间接通过子类构造函数参数传入
{
cout << "I am Son " << endl;
}
};
6、继承与析构函数:先调用子类的析构函数,在调用父类的析构函数;
7、数据成员同名:当父类与子类数据成员名字相同时,在子类内,默认使用的是子类的数据成员,也可通过类名作用域使用父类的数据成员,在父类里同理(类外仅能通过类名作用域区分);函数成员同名:与数据成员一样,也可通过类名作用域区分,但子类父类不构成重载。
8、友元不能被继承 ,静态成员只有一份也不能被继承
多态与虚函数:
1、多态是一种泛型编程思想(同样的代码实现不同的功能),虚函数( virtual)是实现这个思想的语法基础(此处,泛型编程思想的体现是,父类的指针调用子类的函数);
2、父类指针指向子类空间;
CFather*p=new CSon;//父类指针指向子类空间
3、写法:virtual void fun (){ … } 子类若有同名函数必须与父类返回值一致,当参数列表也一致会重写虚表里父类此函数地址为子类的此函数地址,(返回值类型不同只有:子类函数返回值类型为子类引用,父类函数返回值为父类引用,这个情况才被允许。这叫协变)
4、构造函数不能是虚函数,析构函数可以是虚函数;虚函数不属于内联函数(类内函数一般是内联函数,虚函数除外);
5、虚表:虚函数列表,对象空间最开始4字节的内容就是虚表的地址,又叫虚指针,每个类都有一个;
虚表原理:程序执行到 父类里 遇到virtual 就会把这个函数的名字与地址记录在一个列表里,遇到一个记录一个; 当执行到CFather* p = new CSon;时系统会到子类中去寻找与父类同名的虚函数(返回值,参数列表也一致),如果有,则用子类此函数地址覆盖到虚表此函数名下的地址,当执行到p->fun();语句时,系统看这个是虚函数就会跑到虚表里调用此函数名下的地址…
6、虚析构
delete哪个类型的指针(可对指针强制类型转换),就调用谁的析构函数;不管调用谁的析构,这块空间都会被释放掉
多态中,如果释放父类指针,只会调用父类的析构函数;加virtual成了虚析构后,就会子类父类都调用了
7、纯虚函数
形如 virtual void fun () = 0;没有函数实现的函数,有 且 仅有子类实现了这个纯虚函数,父类才能定义对象
抽象 类:有纯虚函数的类就是 抽象 类
接口 类:全是纯虚函数的类,叫接口类
可以有构造函数和成员
(用处,当做一个框架,在不同项目使用到这个框架时,在子类中写当前项目需要的就行)
8、虚继承:多继承(多个基类,子类的子类)中特有的概念,虚基类是为解决多重继承而出现的访问不明确的情况;
其他
1、联编:将模块或函数合并在一起生成可执行代码的处理过程(函数调用);
按联编进行的阶段不同分为静态与动态。
静态联编:编译阶段就将函数实现与函数调用关联(例 普通函数调用)
动态联编:针对C++多态的(C语言全是静态联编)
2、单例:达到一个类只能创建一个对象目的.
当构造函数为private/protected的时候,这个类不能实例化对象,为private时也不能被继承;
因为非静态成员只能通过对象去调用,静态成员可以类名作用域调用,故可以通过静态成员函数申请对象空间,并返回地址。这样依旧可以创建多个指针,可以定义一个静态标记,记录对象的个数,并控制。当第一个指针被delete后再申请一个指针并不能成功,这时可以通过析构函数,将标记清空,以达到重复申请对象的目的。
#include
using namespace std;
class CFather
{
private:
CFather()
{
}
public:
static int Flag ;
static CFather* CreateDL()
{
if (1 == Flag) //标记,限制只能申请一个对象
{
Flag = 0;
return (new CFather);
}
else
{
return NULL;
}
}
~CFather() //清除标记,重复申请对象
{
Flag = 1;
}
};
int CFather::Flag = 1;
int main()
{
CFather* p = CFather::CreateDL(); //申请写法
delete p;
CFather* p1 = CFather::CreateDL(); //重复申请
delete p1;
return 0;
}
3、异常可人为定义,调用abort()函数即可,运行时就会报异常。
另一个异常定义及处理方法:
用throw抛出异常,try去检测,用catch去处理,例:
( throw 抛出对象的时候,要使用引用或指针,不然会调用拷贝构造函数 )
#include
using namespace std;
void fun()
{
cout << "fun\n";
}
void fun1(int x)
{
int a =1;
while (a<=10)
{
if (5 == a)
{
throw a; //抛出异常
}
a++;
}
}
int main()
{
try //检测异常
{
fun1(3); //检测fun1(3)是否有异常
}
catch (int b) //处理异常 异常传递过来 catch可有多个构成重载
{
cout << b << endl;
}
return 0;
}
4、嵌套类:
内部类访问外部类的成员: 在内部类直接定义一个外部类的对象是两个不同的对象,不是同一个对象空间, 若你通过外部类对象修改外部类成员的值,该值不会被传递到内部类里使用外部类的该成员上;可以通过 在内部类中,定义一个外部类的指针成员,通过内部类构造函数给该指针赋值,然后在再通过外部类构造函数给内部类传递this指针 这样在内部类创建的外部类对象与外部类创建的对象就是同一个对象了 ;
(this是C++中的一个关键字,也是一个const指针,它指向当前对象,通过它可以访问当前对象的所有成员。)
#include
using namespace std;
class COuter
{
public:
int outer;
COuter():ci(this)
{
outer = 1;
}
class CInsider
{
public:
int insider;
COuter*p ;
CInsider(COuter*pi):p(pi)
{
insider = 5;
}
void fun()
{
cout << p->outer << endl;
}
};
CInsider ci;
};
int main()
{
COuter cco;
cco.outer = 99;
cco.ci.fun();
return 0;
}
外部类访问内部类的成员:通过外部类定义内部类的对象,然后通过这个对象进行调用就可以了。
5、类型转换 (能不用就不要用)
旧式 :也是我们熟知的强制类型转换,只管转不管对错;
新式:
#include
using namespace std;
class CFather
{
public:
int a;
};
class CSon:public CFather
{
public:
int b;
};
int main()
{
CFather* pf; //父类,子类指针是可以相互转换的(隐式
CSon* ps;
pf = static_cast <CFather*>(ps); //故,可以把子类指针强制转换为父类指针
pf = (CFather*)ps; //旧式 转换,只转不判断 不安全
return 0;
}
const_cast <type> (expression)
例:
const CFather* pf;
CSon* so = const_cast <CSon*> (pf) ; //非法
CFather* pfff = static_cast <CFather*> (pf) ; //合法
3. dynamic_cast 运算符
形式:dynamic_cast< type >(expression);
特点
以上,可理解为 1.父子类指针相互转化的运算符; 2.自己转自己
例如:
CFather* pf = dynamic_cast<CFather*>(son);
reinterpret_cast <type> (expression)
例子:
struct dat
{
short a;
short b;
};
long value = 0xA224B118;
dat* p = reinterpret_cast<dat*>(&value);
cout << hex << p->a;
注意点
1.这种转换适用于依赖实现的底层编程技术,代码不可移植,例如不同的系统可能以不同的顺序存储多字节数据中的字节
2.互相转换时,空间一定要够用,否则不行;
3.函数指针和数据指针不能相互转换;
运算符重载operator
1、打开命名空间也有作用域;
2、operator只针对 类存在的?
3、operator其实是一个函数,用来重定义运算符的函数。
基本写法:
void operator + (Stu& st,int) //返回值, 重载+号
{
//代码块内容自定义
}
4、使用该 运算符 时会自动识别运算操作数类型,调用合适的运算符重载operator来完成运算,若无定义,报错
5、若Operator定义在类内,参数列表(隐式)默认第一个是类的类型。
6、返回值可以放 重定义运算符 运算的结果,就可以连续运算。
7、重载 >>运算符时可以用对象istream成员fail判断,若输入错误… 注:只能类外重载
8、 = [] () -> 只能在类内重载(类的成员)
9、重载++时,前置加加与后缀加加有些区别,区别在参数列表上,后缀加加在参数列表上需加上个int n做标记。
int operator ++(Stu& st1) //类外 前置加加
{
++st1.a;//st1.a+=1;
return st1.a;
}
int operator ++(Stu& st1, int n)//类外 后缀加加 n标记作用
{
return st1.a++;
}
10、重载类型转换(强制类型转换)时,必须写在类内,且函数头不用写返回值类型,却可以有返回值。能不用就不要用,直接 .成员 不更快?
operator int() //强制转换成int类型 注意必须是写在 类内
{
return a;
}
形式:template< typename T> //或template< class T>
例:
template < typename T> //函数模板 关键字template void fun(T a) { cout << "fun1\n"; }
函数模板具体化:当我们指定的特殊类型模板不好处理时, 我们将它拿出来单独处理;
template <> void fun(Str& stt) //可以是引用也可以不是 此处str是结构体名
{
cout << stt.a << endl;
}
实例化:生成指定类型的函数定义(就是用模板可以解决的,你非得拿出来定义一下,但又没有写函数实现的)
例: template void fun< job >(job& j1, job& j2) ; //没有代码块就一个定义 头
可以设定默认值:
template < typename T, typename Y = char >
只有类模板可以有默认值 ; 必须从右向左连续赋值默认类型,跟函数参数默认值一样 ,传递的时候会覆盖掉
创建对象 传递模板参数列表
类模板,需要在类型后加模板参数列表: CFather
fp; 有默认值的时候可以不传类型但是必须要写<> ; 类外,任何位置出现CFather 都要加上模板参数列表;
类外实现的函数模板的写法
写法1:template < classT,class B , class C> void CA
::fun(T a)
{
}
写法2:void CFather::Show()
{
cout << a << endl;
}
直接上例子:
#include
using namespace std;
template <typename T,typename R>
class CFather
{
public:
T a;
CFather(T t) //这个有参数须通过子类构造传递
{
a=t;
}
};
template <typename X,typename Y> //这里不写template 下边就得写具体类型,...就体现不出“模板”
class CSon:public CFather<X,Y> //把X Y传入给T R
{
public:
int aa;
double dd;
CSon(X x)
{
}
};
int main()
{
CFather<char,int> fa(49);
cout << fa.a << endl;
return 0;
}
(父类的指针指向之类的空间)
例:
#include
using namespace std;
template <typename T, typename R>
class CFather
{
public:
T a;
};
template <typename X, typename Y>
class CSon :public CFather<X, Y> //把X Y传入给T R
{
public:
X x;
};
int main()
{
CFather<int ,char>* pf = new CSon<int ,char>; //前后都要传参,且要一致
delete pf;
return 0;
}
子类没有模板(即没写这句:template )
CFather* pf = new CSon;
子类有模板
CFather* pf = new CSon;
#include
using namespace std;
template <typename T>
class CA
{
public:
int a;
};
template <typename T, typename R>
class CFather
{
public:
CFather(CA<char>& aa)
{
}
void fun()
{
cout << "father\n";
}
};
int main()
{
CA<char> aa;
CFather<int ,CA<char>> fa(aa);
return 0;
}
完结,总结来自C3教程