C++中容易混淆的概念

初始化和函数声明

new int[size]();

这种写法表示整个数组初始化为0。因为空括号 (),意味着value initialization,而int属于primitive type,对于primitive type来说,value inialization等同于zaro initialization。(ref: c++11标准5.8.5)

注意:使用空括号()初始化时,前面应该是类型,否则不是初始化,而是声明了一个函数。比如:

C x1; // ok,初始化了一个对象实例。
C x1(); // oops,声明了一个函数原型。
x1 = C(); // ok,初始化了一个匿名实例,然后赋值给x1。

拷贝构造和赋值构造函数

赋值构造函数是个错误的术语!应该称作拷贝赋值函数,因为后者其实并不用于构造。

class A {
    A();
    A(const A&);
    A& operator=(const A&);
};

A a;
A b(a);  // copy ctor
A b = a; // copy ctor!!
b = a;   // copy assignment

实参和形参

  • 实参 argument:传递给函数的参数
  • 形参 parameter:函数定义里声明的参数

形参相当于函数中定义的变量,调用函数传递参数的过程相当于定义形参变量并且用实参的值来初始化。

表达式和语句

C++中的表达式和语句的概念继承自C语言1

表达式是一种有值的语法结构,它由运算符将变量、常量、函数调用返回值结合而成。

  • 变量。变量名本身是一个表达式,表达式的值是变量当前的值。
  • 常量。常量名本身是一个表达式,字面常量也是表达式。对于这两者,表达式的值是常量当前的值。
  • 函数调用。对于返回值不为void的函数,对它的正确调用也是表达式。表达式的值为函数的返回值。
  • 操作符。运算符用于连接表达式中的值。

语句指的是当程序运行时执行某个动作的语法结构。它改变变量的值,产生输出,或处理输入。

C语言包括4类语句:

  • 表达式语句。表达式语句是最简单的一种语句,在表达式的末尾加分号就形成了一个表达式语句。最常用的表达式语句是函数调用语句和赋值语句。
  • 语句块。可以用{ }将一系列语句括起来使其大功能上相当于一条语句,这就是语句块。语句块中可以有变量声明。
  • 空语句。即只有一个分号的语句,它什么也不做。当用在循环体中时,表示循环体什么也不做。
  • 控制语句。控制语句分类3类:循环语句,选择/条件语句,特殊语句。

说明: 任何表达式都有值和类型两个基本属性。理解表达式是理解左值右值的基础。表达式和语句根据语法规则组合(Composition)成更复杂语句和程序。

左值和右值

C++03 3.10/1 提到:Every expression is either an lvalue or an rvalue. 每一个表达式不是lvalue,就是rvalue。所以,左值右值是指表达式(expression),不是对象(object)。

  • 左值是表达式运行后继续存在的对象(persistent object)。例如,obj, *ptr, ptr[idx], ++x。
  • 右值是表达式运行结束就消失的对象。例如,1729, x+y, std::string(“hello”), x++。

简单的判断方式就是:对这个表达式能不能取址操作(address of, &)。如果可以,则是左值,否则为右值。

重载

  • 重载函数
  • 重载操作符
  • 重载函数模板

重载函数

出现在相同作用域内的两个函数,如果具有相同的名字而形参表不同,称作重载函数(overloaded function)。

重载函数在运行时的行为和非重载函数完全一样,主要负担是在编译时来决定该调用哪个实例。

重载函数集合中的全部函数都应该在同一作用域中声明。

比如,一个局部作用域中声明的函数,将隐藏而不是重载那些全局作用域中声明的函数。

#include <string>
void print( const string & );
void print( double ); // overloads print()
void fooBar( int ival ){
    // 独立的域隐藏print()的两个实例
    extern void print( int );
    // 错误: print( const string & )在这个域中被隐藏
    print( "Value : " );
    print( ival ); // ok: print( int ) 可见
}

重载与多态

因为重载只能作用于同一作用域,所以类中的成员函数只能重载自己类的其他成员函数。如果一个派生类中声明了和基类同名的函数,而基类中该函数并非虚函数(virtual),那这不是重载,而是隐藏了基类的同名函数(见Effective C++中有提及);如果是虚函数,那也不称作重载,而是多态。

所以,所谓多态一定是与派生类、虚函数(virtual)在一起的,而不应该与重载相混淆。

using和重载

using声明只是一个声明。由using 声明引入的函数就好像在该声明出现的地方被声明一样,因此由using 声明引入的函数重载了在该声明所出现的域中同名函数的其他声明。而如果using声明引入的函数和该域中的函数声明完全一样,那将是一个编译错误。

extern “C”和重载函数

链接指示符extern "C"只能指定重载函数集中的一个函数。

函数重载解析(function overload resolution)

函数重载解析的步骤简述如下:

  • 确定为该调用而考虑的候选函数,以及函数调用中的实参表属性;
  • 从候选函数中选出可行函数。也就是说,根据调用中指定的实参、实参数目和类型,选择可以被调用的函数;
  • 对于“被用来将实参转换成可行函数参数类型的转换” 划分等级,以便选出与调用最匹配的函数。