(1)必须写在初始化列表中的东东:const变量,引用类型,类中的成员变量是另一个类的类型的成员变量,父类的构造函数。个人认为是这样的,因为这些变量是不得不一开始就初始化的东西。const是一个常量,必须定义的时候就初始化。引用必须在定义的时候就指明要引用谁。另一个类的类型的成员变量,必须要为类进行构造,所以就必须写在参数化列表中。父类就不必再论。我感觉C++之所以这样要求,就是从语法上要求使用者要初始化这些东西,如果这些东西不进行初始化的话,将导致很严重的问题,这样就从语法上保证了安全性。
(2)strcpy的实现
(3)在类A中重载+运算符,只能用"类A+变量“这种方式,而不能使用”变量+类A“,因为第一个参数是this。解决办法将重载+运算符作为类的友元函数,分别重载变量在前类在后,和类在前变量在后的+操作符
(4)重载++或者--,有前置和后置的问题,解决的办法就是在后置自增或者自减运算符要加一个int参数,在调用后置自增或者自减运算符的时候,编译器会自动将这个int值传成0
(5)RTTI包括typeid和dynamic_cast.。typeid返回type_info类的引用。
(6)c++中仿函数和algorithm的使用。
(7)虚析构函数的作用。
(8)堆和栈的区别。http://blog.youkuaiyun.com/hairetz/article/details/4141043
(9)在调用函数的时候,如果是值传递,需要把实参复制一份,压入栈中,而返回值也要赋值一份放在栈中,在函数调用结束时,参数出栈会返回给调用者。
(10)类中静态数据成员是存储在全局/静态存储区中,并不会作为类的对象实例的内存一部分,用sizeof返回的值将不包括这部分数据的大小。类的成员函数也不占类的大小。
(11)浅拷贝可能会导致下面的错误
class A
{
public:
int *pInt;
A()
{
pInt = new int[10];
}
~A()
{
delete pInt;
}
};
A a1 ;
A a2 = a1 ;//因为默认情况下是浅拷贝,a1和a2中的指针指向的是同一块内存,在对象a1和a2析构的时候将调用两次delete,会导致内存区释放两次,导致内存泄露。
(12)创建一个对象分为两个步骤,第一步:首先取得对象所需 的内存。第二步:在该内存块中执行构造函数。(13)虚函数不能是inline函数,原因是虚函数要动态绑定,但是内联函数要将函数调用替换成函数的代码,这不符合动态绑定的要求。即使虚函数被声明为内联函数,编译器遇到这种情况根本不会把这样的函数内联展开,而是当做普通函数来处理。
(14)临时对象时在源码中没有出现的对象,但是在堆栈中产生的未命名对象。产生临时对象一般有两种场合1:当实际调用函数时传入的参数和函数定义中声明的变量类型不匹配(这种情况一般会发生类型转化,将产生一个符合条件的临时对象)2:当函数返回一个对象时
(15)变量优化的一个问题:
#include <iostream>
using namespace std;
class Rational
{
public:
int m ;
int n;
Rational( int a = 0 ,int b = 1 ):m(a),n(b)
{
}
friend const Rational operator+( const Rational& a ,const Rational& b );
};
const Rational operator+( const Rational& a ,const Rational& b )
{
Rational temp;
temp.m = a.m + b.m;
temp.n = a.n + b.n;
return temp;
}
void main()
{
Rational r;
Rational a( 10 , 10 ),b( 5 , 8 );
r = a + b ;
system("pause");
}
可以被这样优化,优化的原则是:尽量将对象延迟到已经确切知道其有效状态时
void main()
{
Rational a( 10 , 10 ),b( 5 , 8 );
Rational r = a + b ;//这样少了一个r的默认构造函数,将会只调用一个Rational的拷贝构造函数
system("pause");
}
还可以这样优化,这样又可以少了一个temp对象的构造
const Rational operator+( const Rational& a ,const Rational& b )
{
return Rational (a.m + b.m,a.n + b.n);
}
在重载=的时候最好也重载+=
Rational& operator+=( const Rational& rhs )
{
m += rhs.m ;
n += rhs.n;
return (*this);
}
如果向执行 Rational a = a + b ;
那么会调用重载的+返回一个临时变量,然后再给a。但是如果这样调用 a+=b;将不会产生一个临时变量,性能要比operator+好,所以尽量使用operator+=。考虑到代码可维护性,尽量利用operator+=来实现operator+
const Rational operator+( const Rational& a ,const Rational& b )
{
return Rational(a)+=b;
}
(16)对于内置类型的i++和++i,其实两者的效率是不同的,‘因为++i是直接将i的值加1之后返回,并不产生临时对象,但是i++要先将原来的值保存到一个临时变量中,然后将值加1,再返回临时对象,这样的话就多了一个临时对象,所以用++i效率会更高。(17)虚函数可能造成的性能损失:(1)构造函数必须初始化vptr(2)虚函数是通过指针间接调用的,所以必须先得到指向虚函数表的指针,然后在获得正确的函数偏移量(3)内联是在编译时决定的,编译器不可能把运行时才解析的虚函数设置成内联函数。 只能在运行期解析的虚函数调用是不允许使用内联 的,这往往 会造成性能问题。因为函数调用的动态绑定是继承的结果,所以消除动态绑定的一种方法是基于模版的设计来代替继承。模版把解析的步骤从运行期间提前到编译期间。
举一个例子:要编写一个实现进程同步的string类,进程同步可以使用:信号量、mutex、和锁机制。可以采用这三种方式:
I:硬编码。分别用这三种方式实现一个可以进程同步的string类。这样的缺点是:代码的重复利用率比较低。优点是:没有虚函数,不需要动态绑定,性能优越。
II:继承。在string类里面保存一个指向进程同步机制父类的指针,以后可以给这个指针传递这三种方式的一种的指针,通过指针调用相应的方法。优点:代码可以重复利用。缺点:利用虚函数,不能内联,性能不优越。
III:模版。可以将要实现进程同步的技术最为string的一个模版参数,传递不同的参数就能够使用不同的技术。优点:既增加了代码的重复利用度,又不需要利用虚函数,影响性能,同时还能够使用内联。
(18)返回值优化(Return Value Optimization,简称RVO),是这么一种优化机制:当函数需要返回一个对象的时候,如果自己创建一个临时对象用户返回,那么这个临时对象会消耗一个构造函数(Constructor)的调用、一个复制构造函数的调用(Copy Constructor)以及一个析构函数(Destructor)的调用的代价。而如果稍微做一点优化,就可以将成本降低到一个构造函数的代价。
Complex operator+( const Complex& a, const Complex& b )
{
Complex retVal;
retVal.real = a.real+b.real;
retVal.imag=a.imag+b.imag;
return retVal
} //未使用RVO
Complex operator+( const Complex& a, const Complex& b )
{
return Complex( a.real+b.real , a.imag+b.imag);
} //使用RVO