网站开发要学什么语言,godaddy加wordpress,外包和劳务派遣哪个好,供电局招聘2023社招条款1#xff1a;仔细区别 pointers 和 references
引用应该被初始化#xff0c;指针可以不被初始化。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。指针与引用的另一个重要的不同是指针可以被重新赋值…条款1仔细区别 pointers 和 references
引用应该被初始化指针可以不被初始化。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是 引用则总是指向在初始化时被指定的对象以后不能改变。 std::string s1(Nancy);std::string s2(Clancy);std::string rs s1; // rs引用s1std::string* ps s1; // ps指向s1rs s2; // rs仍旧引用s1,但是s1的值现在是Clancy
条款2最好使用C转型操作符
这四个操作符是static_cast、const_cast、dynamic_cast、reinterpret_cast。
const_cast 最普通的用途就是转换掉对象的 const 属性dynamic_cast它被用于安全地沿着类的继承关系向下进行类型转换。这就是说你能用 dynamic_cast 把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用而且你能知道转换是否成功。失败的转换将返回空指针当对指针进行类型转换时或者抛出异常当对引用进行类型转换时。它不能被用于缺乏虚函数的类型上。如你想在没有继承关系的类型中进行转换你可能想到 static_cast。reinterpret_cast,使用这个操作符的类型转换其 的 转 换 结 果 几 乎 都 是 执 行 期 定 义 implementation-defined 。 因此使用reinterpret_casts 的代码很难移植。reinterpret_casts 的最普通的用途就是在函数指针类型之间进行转换。 double result static_castdouble(firstNumber)/secondNumber; 条款3绝对不要以多态(polymorphically)方式处理数组
在对数组进行传参使用多态时程序会crash; 因为数组在移位至下一数据时步长是形参(基类)的size而不是指针实际指向数据类型(派生类)的size所以会数组会移位至一个非法的地址 。
#include iostream
using namespace std;class Base
{
public:virtual void test(){coutBase::test()endl;}int a;
};class Derived: public Base
{
public:void test(){coutDerived::test()endl;}int b, c;
};void testArray(Base bArray[], int n)
{for(int i 0; in; i)bArray[i].test(); //i 1时程序crash; 编译器原先已经假设数组中元素
//与Base对象的大小一致但是现在数组中每一个对象大小却与Derived一致,
//派生类的长度比基类要长数组将移动到一个非法位置。
}int main()
{Base *p new Derived[2]; testArray(p, 2);
}
条款4非必要不提供 default construcor
提供无意义的缺省构造函数也会影响类的工作效率。如果成员函数必须测试所有的部分是否都被正确地初始化那么这些函数的调用者就得为此付出更多的时间。而且还得付出更多的代码因为这使得可执行文件或库变得更大。它们也得在测试失败的地方放置代码来处理错误。如果一个类的构造函数能够确保所有的部分被正确初始化所有这些弊病都能够避免。缺省构造函数一般不会提供这种保证所以在它们可能使类变得没有意义时尽量去避免使用它们。
class EquipmentPiece {
public:EquipmentPiece(int IDNumber) {}virtual ~EquipmentPiece() {}int a 1;float b 2.0;
};//避免无用的缺省构造函数int ID1 1, ID2 2;EquipmentPiece bestPieces3[] { EquipmentPiece(ID1), EquipmentPiece(ID2) };
// 正确提供了构造函数的参数// 利用指针数组来代替一个对象数组typedef EquipmentPiece* PEP; // PEP指针指向一个EquipmentPiece对象PEP* bestPieces5 new PEP[10]; // 也正确// 在指针数组里的每一个指针被重新赋值以指向一个不同的EquipmentPiece对象for (int i 0; i 10; i)bestPieces5[i] new EquipmentPiece(ID1);for (int i 0; i 10; i)delete bestPieces5[i];delete bestPieces5;
利用指针数组代替一个对象数组这种方法有两个缺点第一你必须删除数组里每个指针所指向的对象。如果忘了就会发生内存泄漏。第二增加了内存分配量因为正如你需要空间来容纳EquipmentPiece对象一样你也需要空间来容纳指针.
解决办法 //分配足够的 raw memory给一个预备容纳 10 个EquipmentPiece objects 的//数组使用void* rawMemory operator new[](10 * sizeof(EquipmentPiece));//让 basePiece 指向此块内存使这块内存被视为一个 EquipmentPiece 数组EquipmentPiece* bestPieces6 static_castEquipmentPiece*(rawMemory);//利用 “placement new”构造这块内存中的 EquipmentPiece objectsfor (int i 0; i 10; i)new(bestPieces6[i]) EquipmentPiece(i);//将 basePieces 中的各个对象以其构造顺序的相反顺序析构掉for (int i 9; i 0; --i)bestPieces6[i].~EquipmentPiece(); // 如果使用普通的数组删除方法程序的运行将是不可预测的//因为 basePieces 并非来自 new operator//释放 raw memoryoperator delete[](rawMemory);
条款5对定制的“类型转换函数”保持警觉
单自变量 constructors 是指能够以单一自变量成功调用的 constructors。如此的 constructor 可能声明拥有单一参数也可能声明拥有多个参数并且除了第一参数之外都有默认值。
class Name{
public:Name(const string s); //可以把string转换成Name...
};class Rational{
public:Rational(int numerator 0,int denominator 1);//可以把 int 转换成 Rational...
};
有两种函数允许编译器进行这些的转换单参数构造函数(single-argument constructors)和隐式类型转换运算符。
隐式类型转换运算符只是一个样子奇怪的成员函数operator关键字其后跟一个类型符号。你不用定义函数的返回类型因为返回类型就是这个函数的名字。
class Rational {
public:Rational(int numerator 0, int denominator 1) // 转换int到有理数类{n numerator;d denominator;}operator double() const // 转换Rational类成double类型{return static_castdouble(n) / d;}double asDouble() const{return static_castdouble(n) / d;}private:int n, d;
};//谨慎定义类型转换函数Rational r(1, 2); // r的值是1/2double d 0.5 * r; // 转换r到double,然后做乘法fprintf(stdout, value: %f\n, d);std::cout r std::endl; // 应该打印出1/2,但事与愿违,是一个浮点数而不是一个有理数,隐式类型转换的缺点
一般来说越有经验的 C程序员就越喜欢避开类型转换运算符。例子在打印Rational类实例时你忘了为 Rational 对象定义 operator。你可能想打印操作将失败因为没有合适的的 operator被调用。但是你错了。当编译器调用 operator时会发现没有这样的函数存在但是它会试图找到一个合适的隐式类型转换顺序以使得函数调用正常运行。类型转换顺序的规则定义是复杂的但是在现在这种情况下编译器会发现它们能调用Rational::operator double 函数来把 r 转换为 double 类型。所以上述代码打印的结果是一个浮点数而不是一个有理数。这样的函数有时候会引起预料之外的调用。可以用显示的转换函数替代
构造函数用 explicit 声明如果这样做编译器会拒绝为了隐式类型转换而调用构造函数。显式类型转换依然合法。 条款6区别 increament/decrement 操作符的前置prefix和后置postfix形式 参考文章《More Effective C》笔记_more effective c pdf github-CSDN博客
More Effective C-CSDN博客