18、让接口不易被误用
如设计了一个Data的初始化接口:Data(int Mouth, int Day, int Year);
这时可以会出现:Data(30, 12, 2015);的错误;因此可以导入新类型来预防:
struct Mouth
{
explicit Mouth(int M):mouth(M){};
int mouth;
}
这里接口可以成为:
Data(const Mouth& m, const Day& d, const Year& y);
Data(M(12), D(25), Y(2015));
19、类的设计应该想到的问题
20、以const &代替值传递
传值情况:void fun(A a); 传参时,会以另一个对象以拷贝构造函数初始化a,在退出时,会调用析构函数释放a!
传const引用:则没有上述所说的构造、析构函数的调用;传const可以不用担心函数内部对该值会有改变;
传值在继承中还存在问题:如果以子类传递给一个基类,则参数初始化过程调用的只是基类的构造函数,子类部分没有被初始化,无法调用子类的函数;
class base
{
public:
virtual int pintInt()
{
//...
}
};
class derive:public base
{
public:
virtual int printInt()
{
//...
}
};
void CallPrin(base b)// 传值
{
b.pintInt();
}
void f()
{
derive D;
CallPrin(D); //子类初始化基类,非引用传递,只调用了基类的printInt
}
因为引用传递的就是指针,因此对于 内置类型如int或是函数对象、STL的迭代器,传值反而更高效;
21、 函数不要返回指向本地对象的指针或引用
函数返回一个指向函数内生成的本地变量的引用或是指针都是错误的;(栈上的)
derive& CallPrin()
{
derive *pd = new derive; //局部申请的内存(堆)
return *pd; // 返回引用
}
因为上面的返回值是动态分配的,所以用户会经常无法或忘记用delete来释放内存,而造成内存泄漏;
同样,函数也最好不要返回函数内的静态值的引用,否则会出现两次调用函数得到的返回值一样的情况;如
class base
{
public:
int a;
base& operator*(const base& b1)
{
static base b;
cout<<"static b的值: "<<b.a<<endl;
b.a = a * b1.a;
return b;
}
};
void f()
{
base b1,b2,b3;
b1.a = 5;
b2.a = 5;
b3.a = 5;
cout<<"第一次乘的地址"<<&(b1*b2)<<" "<<(b1*b2).a<<endl; //下面两个打印出来 的结果是一样的
cout<<"第二次乘的地址"<<&(b3*b2)<<" "<<(b3*b2).a<<endl;
}
24、二元操作符如果要进行类型转换,应该设为非成员函数(友元)
int & operator*(const int& a, const int & b);
int c = 2*b; //非成员函数可以进行类型转换,成员函数该式子是错误的;’