济源网站建设电话,如何在58同城发布广告,wordpress 横向扩展,怎么写网站建设与运营一. 基础议题
条款1#xff1a;仔细区别 pointers 和 references
这是一个常见的问题。指针#xff08;pointers#xff09;和引用#xff08;references#xff09;都是C面向对象编程中常用的概念#xff0c;虽然他们在某些方面表现得很相似#xff0c;但…一. 基础议题
条款1仔细区别 pointers 和 references
这是一个常见的问题。指针pointers和引用references都是C面向对象编程中常用的概念虽然他们在某些方面表现得很相似但是其实他们有一些重要的区别。
1. 初始化
指针可以在任何时候被初始化而且可以多次重新赋值指向不同的对象或值。引用在创建时必须被初始化并且一旦被初始化后就不能改变目标对象。换句话说引用一旦绑定到一个对象上就不能重新赋值引用到另一个对象。
2. Null值
指针可以为null表示它没有指向任何对象。引用不能为null必须始终链接到一个已经存在的对象。
3. “解引用与间接访问”
指针需要被解引用以访问其指向的值使用*操作符。引用不需要特别的语法就可以访问其关联的对象可以像访问普通变量一样操作引用。
4. 内存
指针本身也有一个内存地址并占据内存空间。引用不占用实际内存只是一个别名。
5. 指针的指针与引用的引用
可以有指向指针的指针甚至有指向指针的指针的指针等等。C规定没有引用的引用。
总的来说如果需要一个变量与另一个变量共享/链接同一值并且不需要修改链接的话引用通常是更好的选择。相反如果你需要存储空值NULL或者需要在不同的时间点指向不同的对象或者更改指向则需要使用指针。
条款2最好使用C转型操作符
C提供了四种类型转换操作符static_castdynamic_castconst_cast和reinterpret_cast。各自都有特定的用途你应该根据具体的情况选择使用哪一种。 static_cast这是最常用的转型操作符可以用于各种简单的、非多态类型之间的转换比如基础数据类型如int到floatfloat到int同一继承层级中的上下转型等。 dynamic_cast主要用于类层次间的向上和向下转换。只有当所涉及的类都是多态的即至少有一个虚函数时才可以使用。如果转换失败dynamic_cast会返回null。 const_cast用于将const对象转为非const对象或者将volatile对象转为非volatile对象。但这并不意味可以通过const_cast修改const对象的值因为那通常會是未定义的行为。 reinterpret_cast进行低层次的类型转换比如将一个指针转换为一个整数或者将一个类型的指针转换为另一个不相关类型的指针。应谨慎使用因为其结果很可能取决于具体实现而且在不同的机器和编译器上可能会有不同的结果。
以上就是C中的四种转型操作符。值得注意的是应该优先使用C的这四种类型转换操作符而不是C风格的类型转换因为C的类型转换操作符更易于找到也更易于理解和控制。
这里再解释一下volatile对象 volatile是C中的一个关键字它用来修饰变量表示这个变量可能会在外部,而非程序代码本身,被意外改变。对于被volatile关键字修饰的变量编译器将会做两件事 它将确保不会因为编译器的优化策略而调整对这个变量的读取和写入的顺序。也就是说编译器不会将这些操作进行缓存或者重排而是每次都会直接在内存中进行。 它通知编译器对这个变量的操作不可省略。也就是说即使同一个值被写入多次或者读取后的值没有被使用编译器都不可以省略这些 seemingly redundant的操作。
常见的场合如下 并发编程当多个线程可能会访问和修改同一个数据项时这个数据项应该被声明为volatile。 硬件访问当一个变量可能被外部之外的事物修改比如硬件寄存器应该将该变量声明为volatile。 用于实现特殊的内存映射例如某些特殊的硬件设备的内存区域。
请注意尽管volatile可以防止编译器的某些优化但它并不是线程安全的解决方案。在多线程环境中进行操作时需要额外的同步工具如互斥锁信号等来保证线程安全。
条款3绝对不要以多态polymorphically方式处理数组
这句话其实是建议我们在使用含有多态性质的数组即数组元素是基类的指针但实际指向不同的子类对象时需要格外小心。
当我们处理传统的数组时编译器会做很多工作来帮助我们例如正确地识别并管理数组中元素的大小和布局。但是当我们使用多态数组时这些编译器的帮助就不再适用我们需要手动管理元素的生命周期包括它们的创建和销毁。
如果我们像处理普通数组一样处理多态数组而没有正确管理这些对象的生命周期就可能会导致一些问题如内存泄漏或者对象的“切片”。
对象的“切片” 是一个常见的问题发生在当我们直接把一个子类对象赋值给一个基类对象时。子类对象的“基类部分”会被复制到基类对象中而子类特有的部分则会被切掉造成数据丢失。
例如以下代码:
Base array[10];
Derived d;array[0] d; // 对象切片, 把派生类对象赋值给基类对象, 丢失了派生类特有的数据。 解决上述问题的常见策略是使用容器类如vector或array存储指向基类的指针并使用new创建对象delete删除对象从而确保每个对象都正确地创建和销毁。
std::vectorBase* array;
array.push_back(new Derived());
...
delete array[0]; // 不能忘记删除对象以防止内存泄漏 所以绝对不要以多态方式处理数组的主要含义是我们在使用多态数组时必须要更加小心以防止上述的问题发生。
条款4非必要不提供default constructor
这句话的含义是除非特别需要否则应避免提供默认构造函数。
在C中默认构造函数是一个没有任何参数的构造函数或者所有参数都有默认值的构造函数。其他语言中同样也有类似的概念。
默认构造函数被调用的情况包括如下几种创建新对象时没有提供任何参数创建类的数组时类的派生类的构造函数中调用基类的构造函数但没有提供任何参数。
通常如果给定的类有一些必须要满足的初始条件或者属性那么为了避免默认构造函数创建出未初始化或者部分初始化的对象我们可能会选择不提供默认构造函数。
比如说假设我们有一个表示分数的类Fraction。每个Fraction对象都有两个成员变量分子numerator和分母denominator。在这个情况下我们可能不希望 提供一个默认构造函数 Fraction()因为这样可能会创建出一个分母为0的对象这在数学上是没有意义的因此我们更希望用户必须手动提供分子和分母。
所以非必要不提供default constructor主要是为了防止创建未初始化或部分初始化的对象提高代码的健壮性。但是具体是否需要提供默认构造函数还需要根据类对象的具体需求来决定。