C++ (自用 精简)

虚函数:

  1. 在基类用virtual声明成员函数为虚函数。
    这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。在类外定义虚函数时,不必再加virtual。
  2. 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。
    C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此在派生类重新声明该虚函数时,可以加virtual,也可以不加,但习惯上一般在每一层声明该函数时都加virtual,使程序更加清晰。如果在派生类中没有对基类的虚函数重新定义,则派生类简单地继承其直接基类的虚函数。
  3. 定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。
  4. 通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。
    通过虚函数与指向基类对象的指针变量的配合使用,就能方便地调用同一类族中不同类的同名函数,只要先用基类指针指向即可。如果指针不断地指向同一类族中不同类的对象,就能不断地调用这些对象中的同名函数。这就如同前面说的,不断地告诉出租车司机要去的目的地,然后司机把你送到你要去的地方。
  5. 类的构造函数不能是虚函数
    • 构造函数是为了构造对象的,所以在调用构造函数时候必然知道是哪个对象调用了构造函数,所以构造函数不能为虚函数。
  6.  类的静态成员函数是虚函数
    • 类的静态成员函数是该类共用的,与该类的对象无关,静态函数里没有this指针,所以不能为虚函数。
    • 虚函数是为了实现动态绑定,不能声明为虚函数的有:
      1、静态成员函数; 2、类外的普通函数; 3、构造函数; 4、友元函数
      此外,还有一些函数可以声明为虚函数,但是没有意义,但编译器不会报错,如:
      1、赋值运算符的重载成员函数: 因为复制操作符的重载函数往往要求形参与类本身的类型一致才能实现函数功能,故形参类型往往是基类的类型,因此即使声明为虚函数,也把虚函数当普通基类普通函数使用。
      2、内联函数:内联函数目的是在代码中直接展开(编译期),而虚函数是为了继承后能动态绑定执行自己的动作(动态绑定),因此本质是矛盾的,因此即使内联函数声明为虚函数,编译器遇到这种情况是不会进行inline展开的,而是当作普通函数来处理。因此声明了虚函数不能实现内敛的,即内敛函数可以声明为虚函数,但是毫无了内联的意义

父类与子类有同名函数时,virtual函数调用次序为指针指向的对象来决定调用哪个,非virtual函数调用次序由指针的类型来决定调用哪个

纯虚函数:

  1、  将成员函数声明为virtual

  2、  后面加上 = 0

  3、  该函数没有函数体, 它的实现留给该基类的派生类去做

例如 :virtual void OnCommand(char* cmdline) = 0;

抽象类:

       含有纯虚函数的类叫做抽象类(纯虚类),抽象类是一种特殊的类,它是为了抽象和设计的目的而建立的,它处于继承层次结构的较上层。

  抽象类不能被实例化,即无法创建该类的对象。抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类没有重新定义纯虚函数,而派生类只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体类了。

内联函数(inline)


内联函数的目的是为了减少函数调用时间。它是把内联函数的函数体在编译器预处理的时候替换到函数调用处,这样代码运行到这里时候就不需要花时间去调用函数。但内联函数有个缺点是它会增加执行文件大小。所以如果不适当的使用内联函数会造成执行文件特别大。
而使用内联函数有以下几点需要注意:
头文件中不仅要包含inline函数的声明,还要包含inline函数的定义
编译器需要把inline函数体替换到函数调用处,所以编译器必须要知道inline函数的函数体是啥,所以要将inline函数的函数定义和函数声明一起写在头文件中,便与编译器查找替换。
可以在同一个项目的不同源文件内定义函数名相同,实现相同的inline函数
同一个inline函数可以多处声明和定义,但是必须要完全相同
定义在class声明内的成员函数默认是inline函数

接口:

       接口描述了类的行为和功能,而不需要完成类的特定实现。

       面向对象的系统可能会使用一个抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,派生类通过继承抽象基类,就把所有类似的操作都继承下来。

外部应用程序提供的功能(即公有函数)在抽象基类中是以纯虚函数的形式存在的。这些纯虚函数在相应的派生类中被实现。

这个架构也使得新的应用程序可以很容易地被添加到系统中,即使是在系统被定义之后依然可以如此。

函数重载运算符重载:

    C++ 允许在同一作用域中的某个函数运算符指定多个定义,函数重载则同名函数的参数不同

运算符重载:Box operator+(const Box&);(其中operator是关键字)

C++ 动态内存:

  • 栈:在函数内部声明的所有变量都将占用栈内存。
  • 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。

new 和 delete :double* pvalue = NULL; 

                           pvalue = new double;     // 为变量请求内存 new char[20](数组)

                            delete pvalue; // 释放内存 delete [] array(数组)

多线程:

  多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程

  • 基于进程的多任务处理是程序的并发执行。
  • 基于线程的多任务处理是同一程序的片段的并发执行。
创建线程:
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 
//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数 
int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
 终止线程: pthread_exit (status)

预处理器:

预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。

所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。比如 #include、#define、#if、#else、#line 等

#define MIN(a,b) (a<b ? a : b)   将 MIN(a,b) 换为(a<b ? a : b) 

# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。#define MKSTR( x ) #x 相当于将x变为 ‘x’传入

## 运算符用于连接两个令牌     #define CONCAT( x, y ) x ## y  相当于CONCAT( xy ) (假定xy=100, 则结果就相当于100)

运算符优先级: 算术运算符>关系运算符>逻辑运算符

if( a+b > c && d-e<f) 不需要用括号调整

delete和delete[]的区别

delete 释放new分配的单个对象指针指向的内存,析构函数调用一次。若数据类型为基础类型时,分配简单类型内存时,内存大小已经确定,系统可以记忆并且进行管理,在析构时,系统并不会调用析构函数,此时与delete[]等效。

delete[] 释放new分配的对象数组指针指向的内存,析构函数调用n次

模板template

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

template <class type> ret-type func-name(parameter list)
{
   // 函数的主体
}

template <typename T>
inline T const& Max (T const& a, T const& b) 
{ 
    return a < b ? b:a; 
} 

友元

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

友元定义的不属于类内,但可以访问类的所有private和ptotected,使用时需要传入具体的对象本身。

friend void printWidth( Box box );

发布了9 篇原创文章 · 获赞 2 · 访问量 6622

猜你喜欢

转载自blog.csdn.net/kz2313456/article/details/88358457
今日推荐