太一的博客

一个程序学徒

从C到C++

const与#define 的区别

  • const 定义的常量有类型,而 #define 定义的没有类型,编译可以对前者进行类型安全检查,而后者仅仅只是做简单替换

  • const 定义的常量在编译时分配内存,而 #define 定义的常量是在预编译时进行替换,不分配内存

  • 作用域不同,const 定义的常变量的作用域为该变量的作用域范围。而 #define 定义的常量作用域为它的定义点到程序结束,当然也可以在某个地方用 #undef 取消

  • 定义常量还可以用 enum,尽量用 constenm 替换 #define 定义常量

  • #define 定义的常量,容易产生副作用

1
2
3
4
5
6
7
8
9
// Effective C++ 3rd的一个例子
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

int a = 5;
int b = 0;
CALL_WITH_MAX(++a, b); // a被累加二次
CALL_WITH_MAX(++a, b+10); // a被累加一次

在这里,调用f之前,a的递增次数竟然取决于”它被拿来和谁比较“

域运算符

  • C++ 中增加的作用域标识符 ::

    • 用于对与局部变量同名的全局变量进行访问
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <iostream>
    #include <stdlib.h>
    using namespace std;

    int a = 1;

    int main() {
    int a = 2;
    cout << a << endl;
    cout << ::a << endl;
    }
    • 用于表示类的成员

new、delete 运算符

  • new 运算符可以用于创建堆空间

  • 成功返回首地址,失败直接抛出异常

  • 语法:

    • 指针变量 = new 数据类型
    • 指针变量 = new 数据类型[长度n]
  • 例如:

    1
    2
    3
    4
    5
    6
    7
    int* p = new int;
    cout << *p << endl;

    int* p2 = new int[10];

    delete p;
    delete[] p2;
  • delete 运算符 可以用于释放堆空间

  • 语法:

    • delete 指针变量;
    • delete []指针变量;
  • 例如:

    • delete p;
    • delete[] pStr;

new、delete都做了什么

  • new 一个新对象
    • 内存分配(operator new
    • 调用构造函数
  • delete 释放一个对象
    • 调用析构函数
    • 释放内存(operator delete

重载

  • 相同的作用域,如果两个函数名称相同,而参数不同,我们把它们称为重载 overload

  • 函数重载又称为函数的多态性

  • 函数重载不同形式:

    • 形参数量不同
    • 形参类型不同
    • 形参的顺序不同
    • 形参数量和形参类型都不同
  • 调用重载函数时,编译器通过检查实际参数的个数、类型和顺序来确定相应的被调用函数

  • 合法的重载例子:

    • int abs(int i);
    • long abs(long l);
    • double abs(double d);
  • 非法的重载例子:

    • int abs(int i);
    • long abs(int i);
    • void abs(int i);
    • 如果返回类型不同而函数名相同、形参也相同,则是不合法的,编译器会报“语法错误”。

name managling 与 extern “C”

  • name managling 这里把它翻译为名字改编
  • C++ 为了支持重载,需要进行 name managling
  • extern “C” 实现 CC++ 混合编程
1
2
3
4
5
6
7
8
#ifdef __cplusplus
extern "C"
{
#endif
...
#ifdef __cplusplus
}
#endif

带默认形参值的函数

  • 函数没有声明时,在函数定义中指定形参的默认值
  • 函数既有定义又有声明时,声明时指定后,定义后就不能再指定默认值

引用

  • 引用是给一个变量起别名

  • 定义引用的一般格式:

    • 类型 &引用名 = 变量名;

    • 例如:

      1
      2
      3
      4
      int a = 1;
      int &b = a;
      // b 是 a 的别名,因此 a 和 b 是同一个单元
      // 注意 定义引用时一定要初始化,指明该引用变量是谁的别名
    • 在实际应用中

注意点:

  • 引用不是变量
  • 引用仅仅只是变量的别名
  • 引用没有自己独立的空间
  • 引用要与它所引用的变量共享空间
  • 对引用所做的改变实际上是对它所引用的变量的改变
  • 引用在定义的时候要进行初始化
  • 引用一经初始化,不能重新指向其他变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

using namespace std;

int main(void)
{
int val = 100;
// int& refval; Error, 引用必须初始化
int& refval = val;
refval = 200; // 将200赋值给refval
// 实际上改变的是val这个变量

cout << "val = " << val << endl;

int val2 = 500;
refval = val2; // 不代表将refval引用至val2这个变量
// 仅仅只是代表将val2赋值给refval

cout << "val = " << val << endl;

return 0;
}

const引用

  • const 引用是指向 const 对象的引用
1
2
3
4
5
6
7
8
9
const int ival = 1024;
const int& refVal = ival; // ok: both reference and object are const
int &ref2 = ival; // error: nonconst reference to a const object

double val3 = 3.14;
const int& ref4 = val3; // 合法的,相当于创建了一个临时变量
// int temp = val3;
// const int& ref4 = temp;

按引用传递

  • 引用传递方式是在函数定义时在形参前面加上引用运算符 &
    • 例如 swap(int &a, int &b);
  • 按值传递方式容易理解,但形参值的改变不能对实参产生影响
  • 地址传递方式通过形参的改变使相应的实参改变,但程序容易产生错误且难以阅读
  • 引用作为参数对形参的任何操作都能改变相应的实参的数据,又使函数调用显得方便、自然
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

void swap(int &x, int &y);

int main() {
int a, b;
a = 10;
b = 20;
swap(a, b);
cout << "a = " << a << " b = " << b << endl;
return 0;
}

void swap(int &x, int &y) {
int temp = x;
x = y;
y = temp;
}

函数作为返回值

  • 引用的另一个作用是用于返回引用的函数
  • 函数返回引用的一个主要目的是可以将函数放在赋值运算符的左边
  • 注意:不能返回对局部变量的引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

// 引用作为函数返回值
//

int a[] = {0, 1, 2, 3, 4};

int& index(int i)
{
return a[i];
}

int main(void)
{

index(3) = 100; // 引用作为函数返回值,使得函数可以放在赋值运算符左边
cout << a[3] << endl;
return 0;
}

引用与指针的区别

  • 引用访问一个变量是直接访问,而指针是间接访问
  • 引用是一个变量的别名,本身不单独分配自己的内存空间,而指针有自己的内存空间
  • 引用一经初始化不能再引用其它变量,而指针可以
  • 尽可能使用引用,不得已时使用指针

值传递:实参要初始化形参时要分配空间,将实参内容拷贝到形参。

引用传递:实参初始化形参时不分配空间

指针传递:本质是值传递,如果我们要修改指针的地址,单纯用指针传递也是不行的