[C++]from C to C++(grammar)

来源:互联网 发布:来自星星的你知我是 编辑:程序博客网 时间:2024/06/05 19:05

本文,直接记录在从C转到C++学习的过程中,了解与区分的语法差异。用于高速补全C++语法点知识,无需参考百科全书。

备注:
  1. 为了达成“速成”的效果,采用菜鸟教程,链接
  2. 结合《Essential C++》来学习。
当然,所谓的速成,仅仅是说从C到C++的语法补充,而不是速成C++,绝对不是这个意思。

C++ 基本语法

C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。
  1. 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。
  2. 类 - 类可以定义为描述对象行为/状态的模板/蓝图。
  3. 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。
  4. 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。
 
命名空间

一般程序,都应该包含namespace std这个命名空间,最常用。
命令行下,执行
$ grep -n "namespace std" /usr/include/c++/6/*
即可发现,std命名空间已经包含非常多内容了。其中包含了标准C,STL等。

引用(reference)与指针(pointer)

指针的算术运算:关键在于,理解指针递增/递减的字节数。或者可以从对象的内存大小理解。
C++的引用与指针,三个区别:链接
  1. 不存在空引用,引用必须链接到一块合法内存,指针可以指向NULL。
  2. 引用不允许改变所代表的对象,指针可以随时修改指向。
  3. 引用必须在创建时被初始化,指针可以随时被初始化。
使用引用原因:
  1. 希望得以直接对传入的对象进行修改。
  2. 降低复制大型对象的格外负担(效率问题)。
引用作为返回值,链接

函数参数的默认值(默认参数值)

使用规则:
  1. 待设置默认值的参数不能是reference(无法被设置为0,一定需指定对象),可以是pointer。
  2. 默认值的解析(resolve)操作由最右开始进行。也就某参数提供默认值的右边的所有参数都应该有默认值。
  3. 默认值只能指定一次,可在函数定义处,可在函数声明处。(为了更高的可见性,将默认值放在函数声明处)
当调用函数时,具备默认值的参数为空,则采用默认值;如果指定了参数的值,则忽略默认值。

函数重载与使用模板

使用场景:
  1. 重载(overlord),其每份实例提供的是相同的通用服务。
  2. 程序代码的主体不变,仅仅改变其中用到的数据类型,可以使用模板。
  3. 两者结合使用(STL库的根基)。

类&对象详解

一个空类,编译器自动定义:构造、析构、拷贝构造、赋值(赋值运算符): [笔记]C++的空类
类访问修饰符类成员可以被定义为 public、private 或 protected。默认情况下是定义为 private。构造函数&析构函数类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。拷贝构造函数拷贝构造函数,是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。友元函数友元函数可以访问类的 private 和 protected 成员。(并不是类的成员函数,也可以是友元类,不止是函数)this指针每个对象都有一个特殊的指针 this,它指向对象本身。(仅成员函数有this指针,友元函数没有)指向类的指针指向类的指针方式如同指向结构的指针。实际上,类可以看成是一个带有函数的结构。类的静态成员类的数据成员和函数成员都可以被声明为静态的。(类的所有实例仅一份副本并共享之,创建第一实例时,所有静态成员都为零,并不能在类的定义中(个人理解为析构函数中))

重载运算符和重载函数

重载函数:参数列表和定义(实现方法不同),但提供相同的服务(函数名相同)。编译器会自动进行重载决策(无法通过返回值进行决策)。
重载运算符:
  1. 重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
  2. 可以重定义或重载大部分 C++ 内置的运算符。这样,就能使用自定义类型的运算符。
实现注意:可重载运算符、不可重载运算符
运算符重载实例:
  1. 一元运算符重载
  2. 二元运算符重载
  3. 关系运算符重载:==和!=可以配置使用
  4. 输入/输出运算符重载
  5. ++ 和 -- 运算符重载
  6. 赋值运算符重载
  7. 函数调用运算符 () 重载
  8. 下标运算符 [] 重载
  9. 类成员访问运算符 -> 重载

构造类class

构造函数constructor与析构函数
constructor:
  1. constructor必须与class同名
  2. constructor不应指定返回值,也没有返回类型,允许有参数
  3. 可overload
  4. initialize stype:
    1. constructor内赋值
    2. constructor成员初始化列表
  5. object实例化时被调用
Triangular::Triangular(int len, int bp)     :_name("Triangular"){     _length = len > 0 ? len : 1;     _beg_pos = bp > 0 ? bp : 1;     _next = _beg_pos - 1;}
destructor:
  1. destructor必须与class同名,并前面条件‘~’前缀
  2. destructor没有返回值,也没有返回类型,同时也没有参数
  3. object被销毁时自动调用
  4. 并非必须,程序设计时根据需要可以省略
注意,易出错:
  1. 成员逐一初始化

可变(mutable)与不可变(const)

     编译器不会对每个函数进行分析,决定它究竟是const还是non-const,但它会检查每个声明为const的member function,看看它们是否真的没有更改class object的内容,如果修改了则编译出错。
int sum(const Triangular &trian){     int beg_pos = trian.beg_pos();     int length = trian.length();     int sum = 0;     for(int ix=0; ix < length; ++ix)          sum += trian.elem(beg_pos+ix);     return sum;}
分析:函数输入const,但调用三个class成员函数是可能会改变class的状态值(成员值/成员变量)
class val_class{     public:          const BigClass& val() const {return _val;}//const version          BigClass& val() {return _val;}//non-const version}void example(const BigClass *pbc, BigClass &rbc){     pbc->val();//call const version     rbv.val();//call non-const version}
分析:根据参数类型(const、non-const),编译器选择调用不同version

继承与多态

简要总结
  1. 继承:使我们得以将一群相关的类组织起来,并让我们得以分享其中的共通数据和操作行为。
  2. 多态:让我们在这些类之上进行编程时,可以如同操控一个单体,而非相互独立的类,并赋予我们更多弹性来加入或移除任何特定类。

最简总结
  1. 继承:享有共通的接口。
  2. 多态:得以用一种类型无关(type-independent)的方式来操作这些对象。

机制实现
  1. 继承机制:定义了父子关系。父类定义了所有子类共通的公有接口和私有实现。每个子类都可以增加或覆盖继承而来的东西,以实现自身独特的行为。
    1. 父类为基类,子类为派生类,父类与子类之间的关系称之为继承体系。
    2. 间接利用指向抽象基类的pointer和reference来操作系统中的各对象,而不是直接操作各个对象。
    3. 主要研究:基类的实现方法被子类继承后,如何被解析(也就如何工作的)
  2. 多态:让基类的pointer或reference得以十分透明地(transparently)指向其中任何一个派生类的对象。其中,这种指向,是又动态绑定(dynamic binding)实现的,“找出基类pointer或reference实际被调用的究竟是哪一个派生类的函数”这一解析操作会延迟至运行时(run-time)才被执行。
    1. 也就是调用基类提供的虚函数接口,实际上被解析为某派生类的具体实现方法。

机制解析
  1. 虚函数机制:

主要解决问题:
  1. 根据经验,思考派生类的操作函数将在何时被解析为(基类/派生类的实现方式)
    1. 通过虚函数机制,动态绑定,解析为派生类实现方式
    2. 通过对象(pointer、reference的类型),静态解析,解析可能为基类或派生类

抽象基类的设计步骤:
  1. 找出所有子类的共通的操作行为。设计出基类的公有接口(public interface)。
  2. 找出那些操作行为与类型有关(tepe-dependent),有哪些操作行为必须根据不同的派生类而有不同的实现方式。这些操作行为应该成为整个类的继承体系中的虚函数。
    1. static member function无法被声明为虚拟函数。
  3. 试着找出每个操作行为的访问层级(access level)。
    1. public:一般程序能够访问的。
    2. private:基类之外不会被使用到的。
    3. protected:可让派生类访问,却不允许一般程序使用。
备注:当我们面临“萃取基类和派生类之间的性质,以决定哪些东西(包括接口和实际成员)属于”谁“时,面向对象设计所面对的挑战,将不只是编程层面而已。这是一个迭代的过程,借由程序设计的经验和用户的反馈,不断演进。




原创粉丝点击