C++学习与总结-基础篇

小结:

  • 用#if 0
    code
    #endif 测试
  • typedef重命名类型
  • 常量:const 和 #define
  • 引用
  • clog
  • 拷贝构造函数
  • 函数重载和运算符重载
  • virtual虚函数,动态链接,数据封装,多态
  • 纯虚函数,接口,抽象类

C++11/14 以前的重要特性:

C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。

四大特性:

  1. 封装
  2. 抽象
  3. 继承
  4. 多态

关于注释

我们还可以使用 #if 0 … #endif 来实现注释,且可以实现嵌套,格式为:

1
2
3
#if 0
code
#endif

你可以把 #if 0 改成 #if 1 来执行 code 的代码。

这种形式对程序调试也可以帮助,测试时使用 #if 1 来执行测试代码,发布后使用 #if 0 来屏蔽测试代码。

#if 后可以是任意的条件语句。

下面的代码如果 condition 条件为 true 执行 code1 ,否则执行 code2。

1
2
3
4
5
#if condition
code1
#else
code2
#endif

关于数据类型

char 一个字节

int 四个字节

float 四个字节

使用 typedef 为一个已有的类型取一个新的名字

关于变量类型

C++ 中有两种类型的表达式:

  • 左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
  • 右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。

关于常量

在 C++ 中,有两种简单的定义常量的方式:

  • 使用 #define 预处理器。
  • 使用 const 关键字。

关于存储类

存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。

使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。

关于运算符

sizeof 返回变量的大小

Condition ? X : Y 如果 Condition 为真 ? 则值为 X : 否则值为 Y。

& 返回变量的地址

* 指向一个变量

关于函数

向函数传递参数的传值调用方法,把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。

向函数传递参数的指针调用方法,把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

向函数传递参数的引用调用方法,把引用的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

当您定义一个函数,您可以为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值。

关于引用

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。

  • 不存在空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
  • 引用必须在创建时被初始化。指针可以在任何时间被初始化。

    C++ 支持把引用作为参数传给函数,这比传一般的参数更安全。

    可以从 C++ 函数中返回引用,就像返回其他数据类型一样。

关于标准输入输出

标准日志流(clog)

预定义的对象 clogiostream 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。

关于类和对象

使用初始化列表来初始化字段

C::C( double a, double b, double c): X(a), Y(b), Z(c)

拷贝构造函数

必须定义拷贝构造函数的情况:

只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义拷贝构造函数也可以拷贝;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义拷贝构造函数。

什么情况使用拷贝构造函数:

类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:

  • (1)一个对象以值传递的方式传入函数体
  • (2)一个对象以值传递的方式从函数返回
  • (3)一个对象需要通过另外一个对象进行初始化。

友元函数

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

因为友元函数没有this指针,则参数要有三种情况:

要访问非static成员时,需要对象做参数;

要访问static成员或全局变量时,则不需要对象做参数;

如果做参数的对象是全局对象,则不需要对象做参数.

可以直接调用友元函数,不需要通过对象或指针

关于继承

多继承(环状继承),A->D, B->D, C->(A,B),例如:

1
2
3
4
class D{......};
class B: public D{......};
class A: public D{......};
class C: public B, public A{.....};

这个继承会使D创建两个对象,要解决上面问题就要用虚拟继承格式

格式:class 类名: virtual 继承方式 父类名

1
2
3
4
class D{......};
class B: virtual public D{......};
class A: virtual public D{......};
class C: public B, public A{.....};

虚继承–(在创建对象的时候会创建一个虚表)在创建父类对象的时候

1
2
A:virtual public D
B:virtual public D

关于重载

C++ 允许在同一作用域中的某个函数运算符指定多个定义,分别称为函数重载运算符重载

重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。

1
Box operator+(const Box&);

关于多态

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定

不添加virtual就是静态多态,或静态链接。

您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。

1
2
// pure virtual function
virtual int area() = 0;

关于数据抽象

用类定义抽象数据类型(ADT)

设计策略

抽象把代码分离为接口和实现。所以在设计组件时,必须保持接口独立于实现,这样,如果改变底层实现,接口也将保持不变。

在这种情况下,不管任何程序使用接口,接口都不会受到影响,只需要将最新的实现重新编译即可。

关于数据封装

C++ 通过创建来支持封装和数据隐藏(public、protected、private)

C++中, 虚函数可以为private, 并且可以被子类覆盖(因为虚函数表的传递),但子类不能调用父类的private虚函数。虚函数的重载性和它声明的权限无关。

关于接口

C++ 接口是使用抽象类来实现的, 如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 “= 0” 来指定的,

如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 “= 0” 来指定的

设计策略

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

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

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