Effective C++

  • 为什么要用const,enum,inline替换#define?

答:首先#define不被视为语言的一部分,不会被处理器识别到,不会进入记号表,有时候会出现微妙的bug,而且#define有时候相比使用const会产生较大的代码量。使用const替换#define,有两种特殊的情况。第一,定义常量指针,常量对象一般被定义在头文件里,定义常量指针,必须满足指针是常量的同时还要满足指针所指之物是常量。第二,class的常量成员,在类中定义常量成员,要满足至多有一份实体,所以同时也要满足static属性,但必须记住在类中只是声明式,而不是定义式,必须在类外进行定义(C++11)。有时候我们可以用枚举类型(enum)来替换#define,为什么要这样做呢?因为有时我们不想让pointer或者reference指向这个常量,所以我们可以这样做。取enum和#define的地址是不合法的。

  • 为什么要尽可能的使用const?

答:首先我们要知道const的作用是什么?const可以修饰外部的全局变量,修饰文件,函数(函数的形参,返回值,成员函数),局部变量,也可以修饰class内部的static和non-static成员变量,还可以修饰指针、迭代器自身,指针、迭代器所指物。使用const可以帮助编译器侦察错误,编译器强制实施按位拷贝,我们可以使用概念上的常量性,当const和non-cosnt成员函数有着实质等价的实现时,可以用non-cosnt替换const避免代码重复。最重要是:const使用在成员函数身上可以对函数进行重载,使用常量引用方式传递对象可以根本上的改变程序效率。

  • 为什么要确认对象被使用前已被初始化?

答:对于全局对象或者函数内,类内,文件类的static对象,不需要担心这个问题,以为它们是存储在静态存储区,会调用default构造函数进行默认的初始化,而对与非这些类型的对象,比如函数内中的局部变量(内置类型),是不会被默认的初始化的,所以我们就要自行的对其初始化。在类中c++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前的,也就是说要求我们最好使用成员初始化列表进行初始化,这样效率会比较高。还要一个问题,有时候我们需要跨编译单元(.cpp)进行初始化(在函数内中),那么怎么确定该对象已经被初始化了?所以该对象应该具有static属性。为什么呢?因为存储在静态存储区的对象,是会被默认初始化的。

  • 为什么要为多态基类声明virtual析构函数?

答:试想基类的指针指向派生类的对象,然后我们想删除此指针,会发生什么事情呢?会发生派生类的基类部分被删除,而派生类本身的成分却没用被删除销毁,这样会出现未定义的行为。所以解决办法就是为基类声明virtual析构函数。当基类的指针被删除时,会发生动态绑定,vptr会指向vtbl,索引出派生类的析构函数,对派生类进行析构,然后会基类进行析构。任何类如果还有virtual函数,那么几乎可以确定它也应该有一个virtual析构函数。但是不是给每个类都要声明virtual析构函数呢?不是!!因为编译器会为我们合成vptr,而且类中还有virtual编译器就会为此类合成vtbl,这样会大大的增加类对象的内存的空间大小,会出现效率低下的问题,所以class的设计目的如果不是作为多态基类,就不要声明vritual析构函数。

  • 为什么不能在构造函数和析构函数中调用virtual函数?

答:理论上是可以的,但是这样写就屏蔽了多态的功能。当派生类声明一个对象时,会调用基类的构造函数,会对派生类的基类部分进行构造,同时也会构造虚指针,虚指针会指向基类的虚函数表。然后派生类调用自己的构造函数,对自己的成员变量进行构造,但是这时的虚指针会指向派生类的虚函数表。但你在基类的构造函数中,调用虚函数时,调用的是基类的虚函数,这时虚函数也就失去了意义。

  • 为什么复制对象勿忘其每一个成分?

答:首先copy构造函数执行的局部拷贝,也就是作用与作用域中(类的作用域。当类含有多个成员变量时,必须为每个成员变量进行构造初始化。当在继承体系中,派生类撰写的copy函数必须复制基类的成分。还有,用构造函数去调用另一个构造函数是不合法的,会出现递归。当某两个构造函数有相同的成分时,应该将共同的机制放进另一个函数中,并由两个构造函数共同调用。

  • 为什么要以对象来管理资源?

答:面对大量数据时,我们new一个对象,但是很有可能最后忘记delete,最后就会导致资源泄露。所以我们必须要使用对象来存储资源,利用对象的析构函数,对资源安全的delete。c++为我们提供智能指针:auto_ptr  shared_prt来管理资源。使用auto_ptr时,如果调用其构造函数或者拷贝赋值运算符,原始对象会变为null,赋值所得的指针会取的其资源的唯一权,这显然是auto_ptr的bug,因此我们经常会使用shared_ptr,其存在引用技术功能。

  •  接口被误用时怎么办呢?

答:当我们给类设计与外界交互的接口时,我们必须要小心的设计,因为有时候会出现接口被误用的危险。出现时我们要怎么办呢?第一:导入新类型可以获得预防。第二:限制类型上的操作,束缚对象值,以及消除客户的资源管理责任。

  • 为什么要以const pass-by-reference替换pass-by-value?

答:pass-by-value是值传递,通常会产生副本,临时对象,这些的产生通常要调用copy构造函数,离开其相应的作用域后,也会调用其对应的析构函数。所以效率会较低。但是const pass-by-reference即引用常量传递便会解决这些问题。有些人会问:为什么是常量呢?因为有时候我们希望对函数传递的对象是不能改变。const pass-by-reference 还有一个优点就是:可以避免对象的切割。为什么呢?因为值传递的化,在继承体系中,派生类传递给基类时,会切割掉派生类对象中的派生类部分。而使用const pass-by-reference 就相当于基类的指针或引用指向派生类,所以会发生动态绑定啦~ 有人还会问?使用值传递一定是不好的吗?不是的! 其实内置类型使用值传递效率还是很高的,为什么呢?因为内置类型相对来说所占内存较小。

  •   函数返回对象时,为什么不能返回局部对象的指针或引用?

答:首相我们应该清楚,引用只是对象的别名,指针只是存储指向该对象的内存空间的地址。当我们返回局部对象的指针或者引用时,局部对象离开其作用域便会被销毁,那么指针所指向的对象便是未定义的,引用所绑定的对象也是不存在的。有人就会说:new一个对象就行啦~。但是谁来delete呢?那么用智能指针吧,杀鸡用牛刀~  不行!哼~ 那么使用static吧,但是效率会低下的。

  •     为什么要将成员变量声明为私有的?

答:如果将成员变量声明为公共的,相对于声明为对外的接口,意味着很难改变。如果要改变变化产生连锁反应,也就是说类的接口可能要全部改变,那样会很麻烦的。而将成员变量声明为私有的就不一样啦~第一:使用类的接口可以对成员变量的处理更加精确的控制。第二:可以为所有可能的实现提供弹性。

  • 为什么尽量少做转型动作?

答:c++中有四种转型。const_cast:改变对象的常量属性。dynamic_cast:执行"安全向下转型"。reinterpret_cast:执行低级转型。static_cast:强迫隐式转换。转型会降低代码的效率,有时候会产生你想象不到的错误。特别是在注重效率的代码中避免dynamic_casts.

  • 为什么应避免返回handles(引用,指针,迭代器)指向内部成分?

答:在讨论此问题之前先说两个事实。第一:成员变量的封装性最多只等于“返回其引用”的函数的访问级别。第二:如果const成员函数传出一个引用,后者所指数据与对象自身有关联,而它又被存储与对象之外,那么这个函数的调用者可以修改那笔数据。当我们返回引用,指针,迭代器指向对象内部时,会降低其封装性。也就是说,我们可以对成员变量进行操作。

  • 为什么虚函数不能是inline函数?

答:虚函数意味等待,直到运行期才确定调用哪个函数,而inline意味“执行前,先将调用动作替换为被调用函数的本体”。

  • 为什么public继承一定要表现出is-a关系?

答:首先我们应该明白继承的含义,所谓继承就是派生类包括了基类的所有接口和实现,也就是说基类身上发生的每一件事情,在派生类身上也会发生,可以理解为派生类是基类,所以public继承意味着is-a(是一种)。

  • 什么是接口继承?什么是实现继承?

答:接口继承就是继承基类的函数声明,实现继承就是继承基类的函数的实体。那么接口继承和实现继承什么时候用呢?声明一个纯虚函数(基类中)可以让派生类只继承函数接口。声明非纯虚函数(基类中)可以让派生类继承函数的接口和缺省实现。声明非虚函数(基类)可以让派生类接触函数的接口和实现(强制性)。

  • 为什么不能重新定义继承而来的缺省参数值?

答:虚函数是通过动态绑定而进行调用的,而缺省参数值是静态绑定的,也就是说在编译时期其类型以及确定。当我们通过使用基类的指针或者引用指向派生类时,会发生动态绑定,此时基类的指针或者引用指向了派生类。如果我们在派生类更改了继承而来的缺省参数值,但是其静态类型没有发生改变,所以缺省参数值是基类函数的缺省参数值。

  • 什么是隐式接口和编译器多态?

答:隐式接口不基于函数签名式,而是由有效表达式组成的。什么意思呢?隐式接口一般出现在c++ template中,只能通过最终template的具现化体现出来。而编译器多态也可以理解为在编译时期哪一个函数将被调用,与运行期多态是不一样的(通过virtual动态绑定实现)。

                          

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值