C++的35个技巧阅读笔记(一)

编程入门 行业动态 更新时间:2024-10-23 11:25:34

C++的35个<a href=https://www.elefans.com/category/jswz/34/1767080.html style=技巧阅读笔记(一)"/>

C++的35个技巧阅读笔记(一)

文章目录

  • 1.仔细区别指针和引用
  • 2.最好使用C++转型操作符
  • 3.绝对不要以多态方式处理数组
  • 4.避免无用的默认构造函数
  • 5.谨慎定义类型转化函数
  • 6.自增,自减操作符前缀和后缀区别
  • 7.千万不要重载&&、|| 和 ,操作符号
  • 8.了解各种不同意义的new和delete
  • 9.利用析构函数避免泄漏资源
  • 10.在构造函数内阻止资源泄漏

1.仔细区别指针和引用

1、引用必须代表某个对象,没有所谓null引用,因此必须有初值。
2、使用引用可能会比使用指针更有效率,因为使用引用之前不需要测试其有效性。
3、指针可以被重新赋值,指向另一个对象,指针 却总是指向(代表)它最初获得的那个对象。

string s1("Nancy");
string s2("Clancy");
string& rs = s1;    //rs代表s1
string *ps = &s1;    //ps指向s1
rs = s2;              //rs任然代表s1,但是s1的值现在变成了“Clancy”
ps = &s2;             //ps现在指向s2,s1没有变化。

使用引用:
1、确定“总是会代表某个对象”,而且“一旦代表了该对象就不能够再改变”。
2、当实现某些操作符时候,例如:operator[] ,必须返回某种“能够被当作assignment赋值对象”的东西:引用。
3、当实现一个操作符而其语法需求无法由pointers 达成,就选择reference,其他任何时候,请采用pointers。
使用指针:
需要考虑“不指向任何对象”的可能性,或考虑“在不同时间指向不同对象”的能力。

不同点:
1、指针是一个变量,引用是别名。
2、对引用求地址,就是对目标变量求地址。即引用名是目标变量名的一个别名。引用在定义上是说引用不占据任何内存空间,但是编译器在一般将其实现为const指针,即指向位置不可变的指针,所以引用实际上与一般指针同样占用内存。。3、指针有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)。
4、“sizeof引用”得到的是所指向的变量(对象)的大小,而”sizeof指针”得到的是指针本身的大小。
5、指针和引用的自增(++)运算意义不一样;
6、引用常见的使用用途:作为函数的参数、函数的返回值。

2.最好使用C++转型操作符

static_cast(常规类型转换):类似C风格类型,具有相同的限制。1、基本数据类型之间的转换 2、用户自定义类型之间,必须有相应构造函数 3、对象指针类型转换,不进行运行时类型检查,不安全 4、任何类型表达式转换成 void类型,不能把struct转换成int,或者把double转换成指针类型,不能移除表达式的常量性。
const_cast(去常量性转换):将某个对象的常量性去掉即:const —> nonconst 而nonconst —> const请使用static_cast。
dynamic_cast(继承转换):执行继承体系中“安全的向下转型或跨系转型动作”,你可以将指向base class objects的pointers或references转型为指向“derived(或sibling base)class objects的pointers或 reference”。如果转型失败,返回一个null指针(当转型对象是pointers)或者一个异常(当转型对象是reference)。
reinterpret_cast(函数指针转换):与编译平台相关,不具移植性,尽量避免使用。

type void (*FuncPtr)();    //FuncPtr 是个指针,指向某个函数,函数无任何参数,返回值为void
FuncPtr funcPtrArray[10];    //funcPtrArray 是个数组,内含10个FuncPtrs
int doSomething();
//funcPtrArray[0] = &doSomething;    //错误,类型不符
funcPtrArry[0] = reinterpret_cast<FuncPtr>(&doSomething);    //这样便可通过编译

3.绝对不要以多态方式处理数组

如果你避免让一个具体类继承自另一个具体类,就不太会犯“以多态方式处理数组”的错误。注意:多态和指针算术不能混用,数组对象几乎总是涉及指针的算术运算,所以数组不要和多态混用。array[i]其实是一个“指针算术表达式”的简写:它代表的其实是*(array + i)。array是个指针,指向数组起始处,array和array+i内存距离i * sizeof(数组中的对象)。

4.避免无用的默认构造函数

凡是“合理地从无到有生成对象”的classes,都应该内含 default constructor,而“必须有某些外来信息才能生成对象”的classses,则不必拥有default constructor。classes 如果缺乏default constructor带来的三个缺点:

  • 1)产生数组时候,一般而言没有任何方法可以为数组中对象指定constructor自变量。
class EquipmentPiece{
public:EquipmentPiece(int IDNumber);...
}
EquipmentPiece bestPieces[10];    //错误!无法调用EquipmentPiece ctors
EquipmentPiece *bestPieces = new EquipmentPiece[10];    //错误!//解决方法,使用 “指针数组” 而非 “对象数组”
typedef EquipmentPiece* PEP;        //PEP是个指向EquipmentPiece的指针
PEP bestPieces[10];             //很好,不需要调用ctor
PEP *bestPieces = new PEP[10];
for(int i = 0; i < 10; ++i)    //为每一个数组成员赋值bestPiece[i] = new EquipmentPiece(ID number);  //此方法有两个缺点,第一:数组所指向的对象必须删除。第二:你需要的内存总量比较大。

可以使用下面方法避免“过度内存使用”,先为数组分配raw memory,然后使用“placement new”在这块内存上构造EquipmentPiece objects。

void *rawMemory = operator new[](10*sizeof(EquipmentPiece));
//让bestPiece指向此块内存,使这块内存被视为一个EquipmentPiece数组
EquipmentPiece *bestPiece = static_cast<EquipmentPiece*>(rawMemory);
//利用“placement new”构造这块内存中的EquipmentPiece objects
for(int i = 0; i < 10; ++i)new (&bestPiece[i])EquipmentPiece(IDNumber);
...
//将bestPieces中各个对象,以其构造顺序的相反顺序析构掉
for(int i = 9; i >= 0; --i)bestPiece[i].~EquipmentPiece();
//释放raw memory
operator delete[](rawMemory);
  • 2)Classes如果缺乏default constructor,带来的第二个缺点是:他们将不适用于许多template-based container classes。
  • 3)Virtual base classes如果缺乏default constructor,必须要求其所有derived classes都必须知道了解其含义,并提供virtual base class的constructor自变量。
    添加无意义的default constructor,也会影响classes的效率,成员函数必须测试所有的部分是否都被正确的初始化。

5.谨慎定义类型转化函数

有两种函数允许编译器作为隐式类型转换之用:单自变量constructor和隐式类型转换操作符。注意:最好不要提供任何类型转换函数,因为在你从未打算也未预期的情况下,此类函数可能会被调用,而结果可能是不正确、不直观的程序行为,很难调试。解决的办法是以功能相等的另一个函数(asdouble()函数)取代类型转换操作符。如果不想进行隐式类型转换,只要将constructor声明为explicit即可,显式类型转换仍是被允许的。

6.自增,自减操作符前缀和后缀区别

class UPInt
{
public:UPInt& operator++();    //前置式++,返回的是一个引用,没有参数。不产生临时变量,效率更高,推荐使用const UPInt operator++(int);    //后置式++,返回的是一个const对象,有一个int类型参数,调用时候编译器默认设置为0。//产生临时变量,不推荐使用。
...
};UPInt& UPInt::operator++()    //前置式:累加然后取出
{*this +=1;return *this;
}const UPInt UPInt::operator++(int)    //后置式:取出然后累加
{UPInt oldVvalue = *this;++(*this);    return oldValue;
}

7.千万不要重载&&、|| 和 ,操作符号

和C一样,C++对于“真假值表达式”采用所谓的“骤死式”评估方式。C++规定不能重载的操作符有:. (点) .*(点星):: (作用域运算符) ?:(三目运算符)new、delete、sizeof、typeid、static_cast、dynamic_cast、const_cast、reinterpret_cast。

8.了解各种不同意义的new和delete

  • 1、new operator:分配足够的内存,用来放置某个对象。调用一个构造函数,为刚才分配的内存中的那个对象设定初值。
  • 2、operator new:唯一的任务就是分配内存。见条款4,函数operator new函数通常声明如下:
void * operator new(size_t size);返回类型为void*,指向一块原始的、未设初值的内存。size_t表示需要分配多少内存。string *ps = new string("Memory Management");
转换为
void* rawMemory = operactor new(sizeof(string));        //获取原始内存放置一个string对象
call string::string("Memory Management") on *memory;    //将内存中的对象初始化
string *ps = static_cast<string*>(rawMemory);            //让ps指向新完成的对象

数组版operator new[]:调用constructor数量更多,注意应该成对使用new和delete。

9.利用析构函数避免泄漏资源

C++标准程序库中auto_ptr(只适用于“单一对象”)的class template。每个auto_ptr的constructor都要求获得一个指向heap object的指针,其destructor会将该heap object删除。只要遵循这个规则:把资源封装在对象内,通常便可以在exception出现时避免资源泄漏。

10.在构造函数内阻止资源泄漏

由于C++不自动清理那些“构造期间抛出exception”的对象,所以你必须设计你的constructor,使他们在那种情况下亦能自我清理。通常只需要将所有异常捕捉起来,执行某种清理工作,然后重新抛出异常,使他继续传播出去即可。

  • 1.常量指针必须通过构造函数初始化成员初值列表加以初始化(见条款12)。
  • 2.处理“构造过程中可能发生的exception”相当棘手,但是使用auto_ptr(以及与auto_ptr相似的classes)可以消除大部分劳役,使用它们不仅能够让代码更容易理解,也使得程序在面对异常时更健壮。
  • 3.C++保证“删除null指针”是安全的。C++只会析构已构造完成的对象,对象只有在其constructor执行完毕才算是完全构造妥当。

更多推荐

C++的35个技巧阅读笔记(一)

本文发布于:2024-03-10 14:53:30,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1728276.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:技巧   笔记

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!