Day1
名字空间: 用来限制作用域(重名问题)
Using namespace std; 全局的声明, 只有一次声明之后使用的 std 中的东西就可以直接用.
::std 全局
Using myspace ::demo 使用 myspace 中的 demo.
换行: endl \n
- cin>> ival>> ch
- cout <<ival << ch << endl
- ------------
Oop 思想: 面向对象的思想
属性 + 行为 = 对象
(记忆 体会 实践 理解 发挥)
类的声明定义:(声明与定义分开)
Class 声明类
Class Demo(类名)
成员的性质(public 公共 ---- private 私有(一般数据私有) ---- protected 保护)
----------
构造函数: 不需要用户来调用它也不能调用, 而是在建立对象时自动执行
构造函数的名字必须与类名同名, 而不能由用户任意命名, 以便以其系统能识别它并把它作为构造函数处理
构造函数的功能是由用户定义的, 用户根据初始化的要求设计函数体和函数参数
如果用户不设计, 则编译器自动生成一个默认构造函数
无返回值
普通默认构造
拷贝构造 用已有的构造新的
深拷贝和浅拷贝 默认的拷贝函数为浅拷贝, 当有指针类成员时一定要自己做拷贝函数做深拷贝(使得数据区域独立 不光是拷贝值)
Demo(const Demo & )
限制构造 类中的成员属于私有或者保护 不允许构造
----------
析构函数: 对象销毁时自动调用(对于同一生命周期的成员 先构造的后析构 )
不能重载
只能用~ Demo()
全局对象, 进程结束时对象自动销毁, 析构自动调用
局部对象, func()调用构造, func 结束析构
静态局部对象, 进程生命期, func 第一次调用构造, 进程结束析构
堆对象, 运行时候构造, 必须是 delete 对象才会销毁, 且自动析构, 如果不 delete 就不析构
------------
This 指针:
指向对象自己
Private 成员 只能在内部使用
Const 成员 不可修改
------------
分析构造和析构的次数
- --------------------------------------------------
- Day2
- Static :
Static 成员 (静态成员是一种特殊成员)
不属于任何类对象(有没有对象都静态成员就已经存在, 且是进程生命期)
作用域在类域
静态的, 唯一的
用于限制作用域
- ------------
- Const :
Const 成员函数 在函数后面加 const, 表示该成员函数不能修改类对象(在这个函数内不能存在修改类对象的任何操作)
只要类方法不修改对象就应该声明为 const
Const 对象 const Demo obj; // 只读对象, 不能被修改(obj 被定义为 Demo 类的只读对象不可以被修改)
只要调用成员函数, 就存在修改对象的风险, 所以不能再调用成员函数. 语法上不允许调用普通成员函数
Const 成员变量 const int x
只读成员变量, 那么必须使用初始化列表进行初始化
Demo::Demo(int a) : x(a) // 初始化列表
只要调用 x 就会检查会不会修改, x 被 const 修饰所以不能被修改.
------------
友元:(打破封装可访问类中的 private protected 对象)
友元的声明可以放在类的私有部分, 也可以放在公有部分, 它们是没有区别的
慎用友元, 存在危险性.
友元函数 一个函数可以是多个类的友元函数, 只需要在各个类中分别声明. 调用与一般函数的调用方法和原理一致
友元类 友元关系不能被继承, 是单向的, 不具有传递性
声明友元类时需要前项声明(前项声明可以不列出具体内容)
友元成员函数 存在递归可能 所以最好声明定义引用分开编写.
前项声明不足以完全说清楚所以分开编写.
- ----------------------------------------------------------------
- Day3
运算符重载:(重载 -- 函数同名, 参数不同, 行为相似)
运算符重载 -- 特殊的一种重载 (加 operator)
友元运算符重载 与函数对比, 功能相同
- friend const int* getaddr(const Demo &);
- friend Demo& addeq(Demo &, const Demo &);
函数实现功能不直观不容易被理解
- friend const int* operator& (const Demo &);
- friend Demo& operator += (Demo &, const Demo &);
运算符重载实现功能直观便于理解
friend Demo& operator ++ (Demo &); 前 ++
friend Demo& operator ++ (Demo &,int); 后 ++
----------
成员函数运算符重载 成员函数在类中 属于类 可以使用 this 指针所以成员函数的运算符重载在传参数时 参数个数和友元运算符重载不同.
bool operator !=(Integer &)
bool 是判断返回值(对或者错)
[] 通过数组名 [] 访问数组成员, 即得到一个对象
通过数组名 [] 访问数组成员, 即得到一个对象,
对象有一个运算符 [] 得到对象管理的整型数组的一个整型
arr[0]:IntArray 对象
arr[0][0]: 是一个整型
------------
注意事项 除关系运算符 ".", 作用域运算符 "::",sizeof 运算符和三目运算符 ":?" 外, C++ 中的所有运算符都可以重载(其中 "=" 和 "&" 不必用户重载)
重载运算符限制限制在 C++ 语言中已经有的运算范围内的允许重载的运算符之中, 不能创建新的运算符.
运算符重载的实质就是函数重载, 遵循函数重载的选择原则
重载之后的运算符不能改变运算符的优先级和结合性, 也不能改变运算符操作数的个数及语法结构
运算符重载不能改变该运算符用于内部类型的对象的含义
运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造, 重载的功能应当与原有功能相类似, 避免没有目的地使用重载运算符
重载运算符的函数不能有默认参数, 否则就改变了运算符的参数个数
重载的运算符只能是用户自定义类型, 否则就不是重载而是改变了现有的 C++ 标准数据类型的运算符的规则
运算符重载可以通过成员函数的形式, 也可以通过友元函数的形式, 和非成员的普通函数
- ----------------------------------------------------
- Day 4
模板: 模板是对一种对类型进行参数化的工具.
模板的声明或者定义只能在全局, 命名空间或者类范围内进行. 即不能在局部范围, 函数内进行.
函数模板 参数类型不一样的但是功能及函数名一样的函数
函数模板的声明定义语法上差异不大, 就像普通函数一样使用
- template
- T add(T x, T y);
- template
- C add(C x, C y)
- {
- return x+y;
- }
类模板 成员属性的类型和成员函数的类不一样但是成员属性及函数一样的类
- template
- class Demo{
- public:
- Demo(T1 a, T2 b);
- void setx(T1 a);
- static void sety(Demo &, T2 val);
- T1 getx() const;
- T2 gety() const;
- private:
- static T1 x;
- T2 y;
- };
友元函数模板
如果一个类是模板类, 又要实现运算符重载, 就是一个友元函数模板;
实质是类模板和函数模板的综合运用
- template
- class Demo{
- public:
- Demo(const int val);
- public:
- T getval() const;
- void setval(const int val);
- template
- friend Demo operator+(const Demo &, const Demo &);
- private:
- T ival;
- };
非类型模板参数
- template
- class Array{
- public:
- Array();
- Array(const Array &);
- ~Array();
- public:
- T& at(const int id=0) const;
- T& operator[](const int id)const;
- private:
- T* const buffer;
- };
T: 模板类型参数; len: 模板非类型参数; T,len 都可以有默认值
- --------------------------------------------
- Day 5
继承: 就是在一个已存在的类的基础上建立一个新的类. 已存在的类 (学生) 称为 "基类"(或父类), 新建立的类 (小学生) 称为 "派生类"(或子类)
1. 派生类继承了基类的所有数据成员和成员函数(private 虽然也继承了, 但是无法直接访问), 并可以对成员作必要的增加和调整.
2. 一个基类可以派生出多个派生类, 每一个派生类又可以作为基类再派生出新的派生类, 因此基类和派生类是相对而言的.
3. 派生类是基类的具体化, 而基类则是派生类的抽象.
继承权限 :
1.public: 公有继承.
a. 公有成员变成派生类的公有成员. b. 保护成员变成派生类的保护成员. c. 私有成员: 只能通过基类接口访问.
2.protected: 保护继承.
a. 公有成员, 保护成员都变成派生类的保护成员 b. 私有成员: 只能通过基类接口访问.
3.private: 私有继承.
a. 公有成员, 保护成员都变成派生类的私有成员 b. 私有成员: 只能通过基类接口访问.
子类的构造析构:
顺序 -- 构造: 先基类, 再派生; 析构: 先派生, 再基类
- (因为派生类的构造和析构 可能会用到基累的成员, 所以派生的构造在基类后, 派生的析构在基类前)
- Is-a
什么是 a (香蕉是水果 水果为 a 是基类 )
Has-a
什么有 a
(思想要理解 咋实践中深入)
多重继承:
class <派生类名> : <继承方式 1> <基类名 1>,<继承方式 2><基类名 2>......
使用需要注意 容易引起歧义
- ----------------------------------------------------------------
- Day 6
多态: 多种状态(一个接口, 多种方法) 程序运行时才决定调用的函数, 是面向对象编程领域的核心概念.
多态是将接口和实现进行分离,(实现以共同的方法, 但因为个体差异, 而采用不同的策略)
多态的应用场景
如果基类实现不是我们想要的方法, 那么在子类派生时, 重写一个新的版本
这样其他成员函数不可以直接复用, 这样的情况就需要虚函数, 即多态
虚函数 子类和基类有相同的方法, 但是行为却有所不同(多态)
(is-a 的关系不是多态, 函数重载 (函数行为相似) 也不是多态)
用 virtual 修饰成员函数(虚函数)
1. 非类的成员函数不能定义为虚函数
2. 类的静态成员函数不能定义为虚函数
3. 构造函数不能定义为虚函数,(析构可以定义为虚函数)
4. 只需要在声明函数的类体中使用关键字 virtual 将函数声明为虚函数, 而定义函数时不需要再使用关键字 virtual
5. 当将基类中的某一成员函数声明为函数后, 派生类中的同名函数 (函数名相同, 参数列表完全一致, 返回值类型相关) 自动成为虚函数
抽象类 隐藏类的其他成员的方法.
1. 含有纯虚函数的类就是抽象类
2. 抽象类没有完整的信息, 只能是派生的基类
3. 抽象类不能有实例, 不能有静态成员
4. 派生类应该实现抽象类的所有方法
5. 抽象类不能定义对象, 因为其纯虚函数只有声明没有定义
6. 抽象类被使用只能派生, 且只有有子类对象
虚析构 由于指针指向的对象是基类类型, 所以 delete 销毁对象的时候, 并不会调用派生类的析构函数, 这样级造成了对象销毁不完整.
工程原则: 只要该类要被继承, 则析构必须是虚函数
构造函数不能为虚, 析构函数一定为虚
virtual ~Base() const = 0 纯虚析构函数
虚继承 解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致的问题, 将共同基类设置为虚基类.
从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝, 同一个函数名也只有一个映射.
typeid(this).name() 类名
联编(链接) 将模板或者函数合并在一起生成可执行代码的处理过程.
静态联编(静态链接) 在编译阶段就将函数实现和函数调用联起来(早绑定)
动态联编(动态链接) 在程序执行的时候才将函数实现和函数调用关联(晚绑定)-- 引入一个虚函数表实现
C++ 中一般情况下的联编指静态联编, 一旦涉及到多态和虚拟函数就必须使用动态联编
重载只是一种语言特性, 编译器根据函数不同的参数表, 把同名函数区分开来属于静态联编, 与多态无关.
- ---------------------------------------------------------
- Day 7
异常: 让一个函数在发现了自己无法处理的错误时抛出 (throw) 一个异常, 然后他的 (直接或间接) 调用者能够处理这个问题.
异常处理机制是一种比较有效的处理系统运行时错误的方法.
在执行程序发生异常时, 可以不在本函数中处理, 而是抛出一个错误信息, 把它传递给上一级的函数来解决, 上一级解决不了再传递给上一级, 一直直到最高一层还无法处理的话, 运行系统会自动调用系统函数 terminate, 由他调用 abort 终止程序.
异常处理 --try(检查)--》throw(抛出)--》catch(捕获)
自定义异常 设计程序时会自定义很多错误, 这些错误在标准错误里是没有的, 所以我们往往需要自定义很多异常类.
自定义异常可以从标准异常 exception 类派生出来 也可以完全自定义一个异常类.
在定义的异常函数后必须加上空异常的关键字 -- 因为异常函数自身需要严格定义不允许异常函数有异常报出.(通用 throw() 空异常 高版本用 nothrow 关键字)
转换函数:
- Int val = 0;
- Char ch = 'a';
- Int main (int argc, char *argv[])
- {
- Val = ch; // 隐式转换
- Ch = char(val) // 显式转换
- }
转换函数的实质为运算符重载, 只是重载的运算符不是内置的运算符而是类
Operator 类型名()
{
实现转换的语句
}
规则 -- 转换函数只能是成员函数, 无返回值, 空参数
不能定义为 void 的转换, 也不允许转换成数组或者函数类型
转换常定义为 const 形式,(转换函数并不改变数据成员的值)
Explicit 关键字 -- 修饰类的构造函数, 被修饰的构造函数的类, 不能发生相应的隐式类型转换
给单参数的构造函数使用 explicit 关键字, 阻止可能产生的隐式转换: 由成员变量类型转换为类类型
来源: http://www.jianshu.com/p/30cfc288523f