面经4

Effective C++

  1. 尽量用编译器替换预处理器,如const\enum\inline替代#define
    2.尽量使用const
    3.编译器会默默为类创建默认构造函数,copy构造函数(A a; A b=a;这里就用到了copy构造函数),copy assignment操作符,析构函数)
    4.如果不想用编译器生成的函数,应该明确拒绝,把不想使用的成员函数声明为private并不予实现
    5.为多态的基类声明virtual析构函数(如果class带有任何virtual函数,那么它就应该设置一个virtual析构函数)
    6.不要再析构函数中出现异常,如果可能会出现异常,那么要么吞下异常,要么终止程序. 如果用户需要对异常做出反应,那么应该用普通函数来进行操作. (在析构函数中发生异常会导致程序过早结束或者出现不确定行为)
    7.绝对不在构造和析构的时候调用虚函数
  2. 成对使用 new 和 delete 时要采取相同形式(new 中使用 [] 则 delete [],new 中不使用 [] 则 delete)
    9.RAII 资源获取即是初始化; 在我们需要用到一些资源的时候,比如需要打开端口,那么我们应该把获取端口和关闭端口都写在对象中,这样对象在创建的时候就能自动获取资源,在结束的时候就能自动释放资源,就不需要我们去查找到底哪些释放了哪些没释放.
    10.用独立的语句将new的对象放入智能指针中,例子
int priority();
void processWidget(std::tr1::shared_ptr<Widget> (new Wdiget),priority());

上面的例子的执行过程可以分成几步
1.调用new Widget
2.执行 priority
3.调用 tr1:shared_ptr构造函数

上面的2的位置可以随意调换,但是如果在中间的时候同时出现异常,那么new Widget返回的指针就会丢失,造成内存泄漏.因此正确的做法应该是将new的语句写成独立语句.

std::tr1::shared_ptr<Widget> pw(new Widget);
process(pw,priority());

11.宁以pass-by-reference-to-const替换pass-by-value

class Person{
    private:
        string name;
        string address;
    public:
        Person(){};
        virtual ~Persion(){};
};
class Student:public Person{
    private:
        string name;
        string address;
    public:
        Student(){};
        ~Student(){};
};

//假设这时候有个函数接口
void PrintStudnt(Student ss);

//如果用下面的方法调用
Student s;
PrintStudent(s);
//那么就需要调用6次构造函数(包括4次string构造函数,person的复制构造函数,student的复制构造函数)同时函数结束后也会调用6次析构函数.可是实际上这个函数只需要s对象的信息,这样完整的拷贝是浪费时间和空间.
//因此可以用C中指针或者C++中引用方式传入对象,实际上这两种方式都是传入了对象的地址
void PrintStudent(Student *ss);
PrintStudent(&s);

void PrintStudent(Student &ss);
PrintStudent(s);

//但是还是有一个风险,我们传入了地址那么就存在函数内部把地址上的内容进行修改的风险.如果不是有意这么做那么这样的做法是非常不好的. 很难察觉到.
//因此会对把传入引用变成常量
void PrintStudent(const Student& ss);
PrintStudent(s);

//还有传入地址能解决切割问题, 因为如果想要实现多态的时候
void PrintStudent(Person ss);
PrintStudent(s);
//上面这种方式会只保留s中基类的内容,派生类多出来的信息就会被切割

//改成传入地址或者引用的方式就不会,编译器只会知道这是一个Person类型的地址, Student的地址也可以套上去
void PrintStudent(const Person &ss);
void PrintStudent(const Persion *ss);

12.返回对象时,别妄想返回其reference


class Rational
{
public:
	Rational(int x, int y) : m_x(x), m_y(y){}
 
	//返回const是属于类型保护,friend修饰,以后条款详说
	friend const Rational operator + (const Rational &lhs, const Rational &rhs)  
	{
		Rational temp(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y); //还有更好的做法
		return temp;
	}
private:
	int m_x;
	int m_y;
};
//上面+的重载,采用的是return value但是这样做会调用拷贝构造函数,和析构哈函数.
//这时候可能会考虑如果用**引用**返回就不用拷贝函数了,但是需要注意的是这里的temp是一个局部变量,是创建在栈空间的,函数执行完后就被回收了,因此这里的饿引用对象就不存在,程序就会报错.
friend const Rational& operator + (const Rational &lhs, const Rational &rhs)  
	{
		Rational temp(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y); //还有更好的做法
		return temp;
	}


//第一种方法还可以在优化,这样省去了调用拷贝构造函数和析构函数的时间,编译器直接创建临时对象并初始化外部变量
friend const Rational operator + (const Rational &lhs, const Rational &rhs)  
	{
		return Rational(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y); //标准做法
	}

//如果在heap里创建对象,用引用返回
friend const Rational& operator + (const Rational &lhs, const Rational &rhs)  
	{
		return *(new Rational(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y));
	}
//这样会出现的问题是,没有合理的办法delete内存,因为没有合理的办法得到返回引用的指针,会导致内存泄漏

//同样的用静态变量也不行 static也会出错
friend const Rational& operator + (const Rational &lhs, const Rational &rhs)  
	{
		static Rational sRational(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y);
		return sRational;
	}

Rational x(1,2), y(3, 4), z(5, 6), w(7, 8);
if ((x + y) == (z + w)) {
    cout<<"equal\n";
}
//这里无论怎么改变数值都是相等的,为什么呢? 因为+返回的是一个引用,当第一次返回x+y时,全局变量区域存的是x+y, 第二次返回z+w的时候,他的返回的对象仍然保存在之前x+y的地方,因此这时候的数值会一直相同.   (静态变量有多线程安全性问题)

13.宁以non-member、non-friend替换member函数

//一个例子引入
class WebBrowser{
  public:
    void clearCache();
    void clearHistory();
    void removeCookies();
    void clearAll();//这个函数包含了上面的三种功能
};
//这时候多了clearAll这个函数就意味着对于这个类的private成分的访问权限大了,也就是类的封装性降低了.因此为了增加封装性会改成下面的写法
void clearBrowser(WebBrowser wb) {
    wb.clearCache();
    wb.clearHistory();
    wb.removeCookies();
}
//使用non-member non-friend函数扩展操作.
  1. 避免使用 handles(包括 引用、指针、迭代器)指向对象内部(以增加封装性、使 const 成员函数的行为更像 const、降低 “虚吊号码牌”(dangling handles,如悬空指针等)的可能性)

  2. 透彻了解 inlining 的里里外外(inlining 在大多数 C++ 程序中是编译期的行为;inline 函数是否真正 inline,取决于编译器;大部分编译器拒绝太过复杂(如带有循环或递归))

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值