3. C++11 的 auto
- 是一个占位符
- 根据 = 右侧的表达式类型自动推导变量类型
- 所以 auto 变量必须初始化
注意:
- 适合: 可以用在迭代器的场景, 可以用在范围 for 循环中;
- 默认情况下, auto 推导出的类型是值类型, 会丢弃引用和 const限定符
- 即使这样, 也不会忽略底层的const(这有些费解, 表层的const和底层的const需要区分开), 比如
const int *p, auto 推导出来是const int *, 我们仍然不能通过这个指针修改指针指向的值
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto v:vec) {
v = v * 2; // 无效修改
}
for (auto &v:vec) {
v = v * 2; // 有效修改
}
std::vector<const int*> cvec = {new int(1), new int(2)};
for (auto p:cvec) {
*p = 10; // 错误, 不能修改 const int
}
}
4. 异常处理
- 错误:
- 语法错误: 编译器检测
- 逻辑错误: 测试
- 异常 Exeption
- 运行环境造成, 比如内存不足, 文件不存在…
- 需要异常处理
- 特征: 可以预见, 无法避免
- 作用: 提高程序的鲁棒性, 分离错误处理代码和正常代码, 提高代码可读性和可维护性
C++ 异常处理机制:(一种专门, 清晰描述异常处理过程的机制)
-
try, 监控,
try{<语句序列>} -
throw, 抛出异常对象,
throw <表达式>; -
catch, 捕获异常,
catch(<类型> <参数名>){<语句序列>} -
如果要throw, 可能是一个临时对象, 这时候要调用拷贝构造函数, (ps: 不要throw 一个指针)
-
catch 捕获异常只需要按照引用类型捕获;
-
一个 try 语句之后可以多个 catch 语句块, 用于捕获不同类型的异常;
-
如果抛出的异常没有被捕获, 那么由系统的 abort 处理
注意 catch 块的顺序
- 如果先基类, 后子类, 那么子类的 catch 块永远不会被执行到;
- 而且, 这时候会出现对象切片问题, 抛出的是子类对象, 但是捕获的是基类对象, 只能捕获到基类的部分
无参数的throw
throw;重新抛出当前捕获的异常catch(...) { ... throw; }
5. IO 处理 (虚化全局函数)
- 操作符 << 和 >> 重载
- 全局函数没有多态性,
CPoint2D,CPoint3D,<<和>>只能作为全局函数重载, 我们首先写出一种存在问题的方案:
class Cpoint2D {
double x, y;
public:
Cpoint2D(double x=0, double y=0): x(x), y(y) {}
friend std::ostream& operator<<(std::ostream &out, const Cpoint2D &p);
};
std::ostream& operator<<(std::ostream &out, const Cpoint2D &p) {
out << "(" << p.x << ", " << p.y << ")";
return out;
}
class Cpoint3D : public Cpoint2D {
double z;
public:
Cpoint3D(double x=0, double y=0, double z=0): Cpoint2D(x, y), z(z) {}
friend std::ostream& operator<<(std::ostream &out, const Cpoint3D &p);
};
std::ostream& operator<<(std::ostream &out, const Cpoint3D &p) {
out << "(" << p.x << ", " << p.y << ", " << p.z << ")";
return out;
}
这个时候的问题在于, 如果我们有一个 Cpoint2D* p = new Cpoint3D(1,2,3); cout << *p;, 这个时候调用的还是 Cpoint2D 的 operator<<, 因为全局函数没有多态性;
- 解决方案: 这时只能虚化非成员函数, non-virtual interface, NVI
class Cpoint2D { double x, y; public: Cpoint2D(double x=0, double y=0): x(x), y(y) {} virtual void display(ostream &out) { out << "(" << x << ", " << y << ")"; } }; std::ostream& operator<<(std::ostream &out, const Cpoint2D &p) { p.display(out); return out; } class Cpoint3D : public Cpoint2D { double z; public: Cpoint3D(double x=0, double y=0, double z=0): Cpoint2D(x, y), z(z) {} void display(ostream &out) override { out << "(" << x << ", " << y << ", " << z << ")"; } }; ostream& operator<<(ostream &out, const Cpoint3D &p) { p.display(out); return out; } - 这样就可以实现多态性了
虚的构造函数, 包含例子
我们由 NewsLetter 报纸类, NLComponent 抽象类, TextBlock 文字类和 Graphic图像类都继承 NLComponent
class NLComponent{...};
class TextBlock : public NLComponent {...};
class Graphic : public NLComponent {...};
class NewsLetter {
public:
NewsLetter(istream &in);
NewsLetter(const NewsLetter &rhs);
private:
list<NLComponent*> components;
};
// 关键问题就在这里, 如何实现拷贝构造函数
NewsLetter::NewsLetter(const NewsLetter &rhs) {
for (NLComponent* comp : rhs.components) {
components.push_back(???); // 如何多态性的构造对象, new TextBlock? new Graphic?
}
}
解决方案, 在 NLComponent 中定义一个虚的克隆函数 clone, 由子类实现具体的克隆逻辑
virtual NLComponent* clone() const = 0; // 纯虚函数
virtual TextBlock* clone() const override {
return new TextBlock(*this); // 调用拷贝构造函数
}
virtual Graphic* clone() const override {
return new Graphic(*this); // 调用拷贝构造函数
}
// 在 NewsLetter 的拷贝构造函数中调用 clone
NewsLetter::NewsLetter(const NewsLetter &rhs) {
for (NLComponent* comp : rhs.components) {
components.push_back(comp->clone()); // 多态性地克隆对象
}
}
BST array 和 balancedBST array, 多态编程不能直接使用对象数组
class BST{...};
class balancedBST : public BST{...};
void printBSTArray(ostream &s, const BST array[], int size) {
for (int i=0; i<size; i++) {
s << array[i]; // 调用 operator<<
}
}
BalancedBST bbstArray[10];
printBSTArray(cout, bbstArray, 10); // 错误, 切片对象
- 如果使用指针数组, 那么可以就实现多态

350

被折叠的 条评论
为什么被折叠?



