C/C++ 一点笔记(2)
继承和派生:
(1)子类不加修改的延续父类的特征,我们把它叫做继承。
(2)在原有基础上建立新类并且添加新新征的过程叫做“类的派生”。
(3)把原有的类叫做“基类”,又叫“父类”,把新建的类叫做“派生类”,又叫子类。
例如:class Son : public Father
(4)公有派生的公有成员仍然为公有成员;公有派生的保护成员仍然为保护成员;公有派生的私有成员是不能为派生访问的。
(5)私有继承:class Son : private Father
私有方式派生的子类,父类的公有和保护成员在子类中是私有的,而私有成员是不可访问的;由于私有派生不利于继续派生,所以实际中用的不多。不管以仅有还是私有形式继承,基类的私有成员在派生类都是不可访问的。
(6)单一继承:只有一个基类
多重继承:拥有多个基类(C#中没有多重继承)
例如:class Son : public Father,public Mother
(7)子类调用顺序:构造父类对象→构造子类对象→析构子类对象→析构父类对象
当一个类中的包含子对象时,构造函数的调用 顺序是先调用子对象的构造函数(按照它们在类定义时出现的顺序,而不是按照构造列表中出现的顺序),然后再调用当前对象的构造函数代码,调用析构函数的顺序与构造的顺序刚好相反。
虚函数:
(1)在不使用virtual之前,C++对重载的函数使用静态联编,而使用virtual以后,C++则对该函数进行动态联编。
(2)静态联编是指联编工作出现在编译连接阶段,这种联编又称早期联编,它解决了程序中的调用与执行该操作代码间的关系。在编译时所进行的这种联编又称静态束定,在编译时就解决了程序中操作调用与执行该操作代码的关系。对于静态联编来说。由于已经确定了调用和被调用间的关系。对于静态联编来说,由于已经确定了调用和被调用的关系,因此代码在编译时与执行时的效果都是一样的。
(3)在程序执行时进行联编工作的被称为动态联编,或称动态束定,又叫晚期联编。
(4)假如在虚函数中没有采用指针或者引用,那么就无法实现动态联编。
(5)在虚函数中使用成员名限定(例如:A::get)可以强行解除动态联编。
虚析构函数:
(1)一个派生类对象在创建时首先会调用基类的构造函数,然后调用该类的构造函数。一般情况下,在使用虚函数的时候,我们都会将派生对象的的指针传递给指向基类的指针,那么假如指向派生类的对象的指针删除时会发生什么情况呢?如果析构函数是虚函数,那么就会进行正确的操作,它会先调用派生类的析构函数,由于一般情况下任何类的析构函数都可以声明为虚析构函数,当指针删除时,系统就会获得对象进行时的类型并调用正确的析构函数。
(2)由于析构函数不允许带有参数,因此它不可能实现重载,那么一个类就只能有一个虚析构函数。
(3)只要基类的析构函数被说明为虚析构函数,那么派生类的析构函数无论说明与否,都自然成为虚析构函数。
(4)在C++中虚构造函数是不存在的,因此也无法声明。
(5)如果基类中定义了虚函数,析构函数也应说明为虚析构函数,这样对内存的回收会更准确些。
函数模板(STL的基础)
(1)看下列例子:求绝对值的例子
int abs(int x) { return x<0?-x:x; }
double abs(double x) { return x<0;-x:x; }
这两个函数除了参数类型不同,功能完全一样,这个时候,就可以把这些功能相同、参数类型不同的函数抽象成一个模板,这就是函数模板。
函数模板的定义形式:template <typename 标识符>,关键字typename,也可以由关键字class代替函数定义部分。
所以上面求绝对值可以写成:
template <typename T> T abs(T x) { return x<0?-x:x; }
注意:编译器会在函数调用时推导出函数模板的参数类型。记住是在编译时就已经推导出参数类型的代码,而不是在运行时才推导参数类型。
下面是一个完整的函数模板应用的例子:
template <class T> T abs(T x) { return x<0 ? -x : x; }
template <class T> T max(T tl,T t2) { return (t1 > t2) ? t1 : t2; }
int main() { int a=-1; int b=-2; int c
abs(a); abs(b) c=max(a,b);
return 0; }