一. 课程概述

  1. 引用数据类型
  2. 面向对象设计
  3. 智能指针
  4. 设计模式
  5. 容器和算法
  6. 二叉树
  7. 回溯
  8. 排序

二. std::cout

#include <iostream>                                                                                                                                                   

int main(void){
    std::cout << "hello, world\n";
    return 0;
}

2.1 命名空间

命名空间为防止名字冲突提供了更加可靠的机制。

命名空间分割了全局命名空间,其中的每个命名空间都是一个作用域

2.2 命名空间定义

#include <iostream>

namespace alice{
    int num_1 = 1;
}

namespace jack{
    int num_1 = 2;
}

int num_1 = 3;

int main(void){
    std::cout << alice::num_1 << '\n';
    std::cout << jack::num_1 << '\n';
    std::cout << num_1 << '\n';
    return 0;
}

定义在某个命名空间中的名字可以被该命名空间内的其它成员直接访问

位于该命名空间之外的代码则必须明确指出所用的名字属于哪个命名空间

2.3 不连续性

命名空间可以定义在几个不同的部分。

例如,定义一个 alice 的命名空间,

  1. 如果之前没有名为 alice 的命名空间,那么就创建一个新的命名空间
  2. 否则打开 alice 这个命名空间,并为其添加一些新成员的声明。

这个特性使得我们可以将几个独立的接口和实现文件组成一个命名空间

C++ 标准库中的所有函数和类都是在命名空间 std 中定义的

三. 初探"引用"

引用其实就是为对象起了另外一个名字,它并没有创建新的对象

引用变量必须在声明的时候同时进行初始化,不可以先声明再赋值

引用可以分为左值引用、右值引用和万能引用

3.1 左值和右值

在 C++ 中,左值是指一个指向特定内存的具有名称的值,它有一个相对稳定的内存地址,并且有一段较长的生命周期

右值则是不指向稳定内存地址的匿名值,它的生命周期很短,通常是暂时性的

基于这一特征,我们可以用取地址符来判断左值和右值,能取到地址的是左值,否则为右值

通常字面量都是右值,除了字符串字面量,因为编译器会将字符串字面量存储到程序的数据段中,程序加载的时候也会为其开辟内存空间

3.2 左值引用

非常量的左值引用只能引用一个左值。常量左值引用,既能引用左值,又能引用右值,只是不能修改所引用的右值

#include <iostream>

int main(void){
    int num = 1;
    int &num_y_1 = num;
    std::cout << num_y_1 << '\n';

    const int &num_y_2 = 999;
    std::cout << num_y_2 << '\n';
    return 0;
}
#include <iostream>

typedef struct{
    char name[12];
    char gender[12];
    int age;
}Person;

void printPerson(const Person &person){
    std::cout << person.name << " " << person.gender << " " << person.age << '\n';
    return;
}

int main(void){
    Person person_1 = {"alice", "female", 18};
    printPerson(person_1);
    return 0;                                                                                                                                                         
}

3.3 为什么需要引用

引用是专为类设计的,目的是为了减少临时变量的拷贝,从而获得性能上的提升。

那么什么是临时变量?右值引用是什么?万能引用是什么?它们是用来解决什么问题的?我们以后再讲

四. 函数的默认实参

某些函数有这样一种形参,在函数的很多次调用中,它们都被赋予一个相同的值,此时我们可以把这个反复出现的值称为函数的默认实参。

调用含有默认实参的函数时,可以包含实参,也可以省略该实参

#include <iostream>

void sendMessage(const char* name = "Alice", const char* message = "hello, world"){
    std::cout << name << ": " << message << '\n';
}

int main(void){
    sendMessage();
    sendMessage("Jack");
    sendMessage("Alice", "What's your name?");
    return 0;
}

注意:一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值

因此当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序,尽量让不怎么使用默认值的形参出现在前面,而让那些经常使用默认值的形参出现在后面

五. 函数的重载

如果同一作用域中的几个函数名字相同但形参列表不同,我们称之为重载函数

注意:main 函数不能重载

Record lookup(const Account &);
Record lookup(const Phone&);
Record loopup(const Name&);

对于重载函数来说,它们应该在形参数量或形参类型上有所不同;不允许两个函数除了返回值类型外,其它所有要素都相同

  1. 如果形参是某种类型,而非引用或指针,那么有 const 的形参无法与一个没有 const 的形参区分开
  2. 如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载

下图代码会报错

#include <iostream>

void print(int num){
    std::cout << "int\n"; 
}

void print(const int num){
    std::cout << "const int\n";
}

int main(void){
    return 0;
}
#include <iostream>

void print(int *num){
    std::cout << "int*\n";
}

void print(const int *num){
    std::cout << "const int*\n";
}

void print(int &num){
    std::cout << "int &\n";
}

void print(const int &num){
    std::cout << "const int &\n";
}

int main(void){
    int num_1 = 1;
    const int num_2 = 2;

    print(&num_1);
    print(&num_2);
   
    print(num_1);
    print(num_2);
    return 0;
}

尽管重载函数能在一定程度上减轻我们为函数起名字、记名字的负担,但是最好只重载那些确实非常相似的操作

作业

罗马数字包含以下七种字符: I, V, X, LCD 和 M

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做  XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  1. I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  2. X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
  3. C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给你一个整数,将其转为罗马数字。