高端品牌网站设计,天津比较好的设计公司,营销方式都有哪些,丽水品牌网站设计参考资料#xff1a;
《Effective C》第三版
注意#xff1a;《Effective C》不涉及任何 C11 的内容#xff0c;因此其中的部分准则可能在 C11 出现后有更好的实现方式。
条款 18#xff1a;让接口容易被正确使用#xff0c;不易被误用
好的接口很容易被正确使用…参考资料
《Effective C》第三版
注意《Effective C》不涉及任何 C11 的内容因此其中的部分准则可能在 C11 出现后有更好的实现方式。
条款 18让接口容易被正确使用不易被误用
好的接口很容易被正确使用不容易被误用。你应该在你的接口里努力达成这一性质
理想状态下应该在编译期发现客户对接口的误用。
“促进正确使用”的办法包括接口的一致性、以及与内置类型的行为兼容
如果没有特殊理由尽量令你的 types 和内置类型保持一致。
“阻止误用的办法”的办法包括建立新类型、限制类型上的操作束缚对象值以及消除客户的资源管理责任
假设我们有设计一个用来表示日期的类
class Date {
public:Date(int month, int day, int year);
private:int month, day, year;
};这样的接口是很容易被误用的
Date(30, 3, 2024); // 错误的参数传递顺序
Date(2, 30, 2024); // 错误数据范围一种常见的预防方法是导入新类型
class Month {
public:explicit Month(int val);
private:int val;
};class Date {
public:Date(const Month m, const Day d, const Year y);...
};Date(Day(30), Month(3), Year(2024)); // 编译错误
设计了类型就可以限制每个类型的合法值比较安全的做法是预先用函数定义有效值
class Month {
public:static Month Jan() { return Month(1) }...
private:explicit Month(int val); // 私有构造函数避免用户调用
};Date(Month::Jan(), day(30), Year(2024));
如果我们的接口要求客户必须“记得某些事情”就是有着“不正确”使用的倾向例如下面的工厂函数, 返回一个指针指向动态分配对象
A* createA(...);为了避免资源泄露客户可以将 createA 返回的指针保存在智能指针中但客户可能会忘记这一点所以我们最好令工厂函数直接返回智能指针
shared_ptrA* createA(...);shared_ptr 支持定制删除器, 可以防范 DLL 问题
shared_ptr 有一个特别好的性质它会自动使用它所管理指针的专属删除器自定义的删除器或默认的 delete这可以解决“cross-DLL problem”。“cross-DLL problem”指对象在一个动态链接库DLL被 new 创建在另一个 DLL 被 delete 销毁。
条款 19设计 class 犹如设计 type
Class 的设计就是 type 的设计在定义一个新 type 之前请确定你仔细思考过本条款覆盖的所有讨论主题
每次设计 class 时需要考虑如下问题
新 type 的对象应该被如何创建和销毁对象的初始化和对象的赋值该有什么样的差别新 type 的对象如果被 passed by value意味着什么什么是新 type 的“合法值”你的新 type 需要配合某个继承体系吗你的新 type 需要什么样的转换什么样的操作符和函数对此新 type 而言是合理的什么样的标准函数应该被驳回谁该取用新 type 的成员什么是新 type 的未声明接口你的新 type 有多么一般化你真的需要一个新 type 吗
条款 20宁以 pass-by-reference-to-const 替换 pass-by-value
尽量以 pass-by-reference-to-const 替换 pass-by-value。前者通常比较高效并可避免切割问题
考虑下面的例子
class A{
public:...
private:string a, b ,c;
}void func(A a);执行 func 在参数构造时需要调用 A 的 copy 构造函数进而需要调用 3 个 string 的 copy 构造函数执行结束后这些对象还要析构。更高效的方法是使用 pass-by-conference-to-const避免了对象的构造和析构同时 const 也保证 func 不会对传入的对象进行修改。
pass-by-reference-to-const 还可以避免切割问题
void func(base b){b.f(); // 调用base::f()
}void func(cosnt base b){b.f(); // 根据实际传入的类型执行不同版本的f()
}以上规则并不适用于内置类型、STL 的迭代器和函数对象。对它们而言pass-by-value 比较合适
引用的底层实现往往是指针所以对于内置类型pass-by-value 往往比 pass-by-reference-to-const 更高效。此外STL 的迭代器和函数对象习惯上实现为 pass-by-valueSTL 的实现者保证了高效性和避免出现切割问题。
需要注意的是不能因为内置类型适合 pass-by-value就认为和内置类型一样小的对象适合 pass-by-value原因是
对象小不一定意味着构造函数不昂贵。编译器对待内置类型和自定义类型的方式截然不同例如某些编译器会把 double 对象放入缓存中却拒绝把只含有一个 double 成员的自定义对象加入缓存。自定义对象的大小容易有所变化。
条款 21必须返回对象时别妄想返回其 reference
绝不要返回 pointer 或 reference 指向一个 local stack或返回 reference 指向一个 heap-allocated 对象或返回 pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象
考虑我们有一个表示有理数的类重载了 *
class Rational {
public:Rational(int n, int d);// 试图by reference返回值friend const Rational operator*(const Rational lhs, const Rational rhs);
private:int n, d;
};返回指向 local stack 的 pointer 或 reference由于 local stack 对象在函数返回的时候就已经被销毁了所以任何使用返回值的行为都将导致未定义行为。
返回指向 heap-allocated 的 pointer 或 reference调用者很容易忘记 delete很容易造成内存泄露。
如果考虑定义一个静态 Rational 对象专门保存结果不仅会在多线程产生不安全的问题有时还会造成逻辑错误
bool operator(const Rational lhs, const Rational rhs);
Rational a, b, c, d;
if ((a * b) (c * d)) {...
}
由于 a*b 和 c*d 返回的是同一个静态变量所以条件永远成立。
条款 22将成员变量声明为 private
切记将成员变量声明为 private这可赋予客户访问数据的一致性可细微划分访问控制、允诺约束条件获得保证并提供 class 作者以充分实现弹性
所有成员变量都不该是 public
从语法一致性的角度来看所有变量不是 public意味着所有接口都是函数此时用户就不需要纠结是否应该使用小括号。使用函数可以让你对成员变量有更精确的控制通过函数可以实现“不可访问”、“只读访问”、“只写访问”、“读写访问”等。从封装性的角度来看将成员变量隐藏在函数接口的背后可以使实现更加灵活如在成员变量被读写时进行记录、验证成员变量是否满足约束条件等。此外不封装通常也意味着不可改变将成员变量隐藏就保留了优化的空间。
protected 并不比 public 更具封装性
使用 protected 虽然相比 public 可以实现语法一致和精确控制但其封装性却并不比 public 强。
成员变量的封装性与改变例如从 class 中移除这个成员变量所破坏的代码量成反比。对于 public 变量一旦其被移除所有使用它的用户代码都会被破坏对于 protected 变量一旦被移除所有使用它的 derived class 代码都会被破坏二者都会造成不可预知的大量代码受到破坏。
所以从封装的角度来看只有 private封装和其他不封装。