C++ note (3)
Eiaton

数据抽象和类 Ⅱ

C++ 新增

新类型:bool

C

  • 没有 bool 类型
    1
    2
    #define true = 1
    #define false = 0
  • C99 定义了 _Bool 类型,并通过 stdbool.h 实现与 C++ 兼容

C++

  • 定义了三个关键字:booltruefalse
  • 当显式(例 (bool)7)或隐式(例 bool b = 7;)转为 bool 类型时
    • 0 值转为 false
    • 0 值转为 true
      1
      2
      cout << true //输出 1
      cout << false //输出 0

形参:void

C

  • fn(); 没有声明形参表示函数的形参不确定
  • 没有参数,则必须显式声明 fn(void);

C++

  • fn(); 等价于 fn(void);
  • 使用 表示可变参数
    1
    2
    3
    4
    5
    int printx(const char* fmt, ...);
    // 能以一个或多个实参调用:

    printx("hello world");
    printx("a = %d b = %d", a, b);

新特性

函数重载

背景:

  • 在开发中,需要的函数功能类似,但参数数量或类型不同
    C 的解决:
  • 必须申明两个函数(不重名)
    C++ 的解决:
  • 声明同名函数,但参数类型不同:
    1
    2
    3
    void swap(int& a, int& b);

    void swap(double& a, double& b);

auto 用于函数重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void swap(int& a, int& b) {
auto temp = a;
a = b;
b = temp;
}

void swap(double& a, double& b) {
auto temp = a;
a = b;
b = temp;
}

int main(void) {
int i = 0;
int j = 1;
double p =0.1;
double q = 1.1;
swap(i, j);
swap(p, q);
}

让编译推导决定使用哪个函数

事实上,编译器会将函数名、参数数量、参数类型编译为唯一的内部函数名

  • swap_int_int
  • swap_double_double

是不同的函数签名

默认实参

C

  • 不支持函数默认参数和值

C++

  • 默认参数只能定义在参数右边
  • 一个声明和实现可匹配多个函数
    • fn() fn(int) fun(int, float) fn(int, float, char)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      void fn(int n = 1, float b = 1.2, char c = '@'); //实现
      void fn(int n, float b, char c){
          cout << n << ", " << b << ", " << c << endl;
      }
      int main(){
          fn(); //func(1,1.2,'@')
          fn(10); //func(10,1.2,'@')
          fn(20, 9.8); //func(20,9.8,'@')
          fn(30, 3.5, '#'); //func(30,3.5,'#')
          return (0);
      }

字符串类型:string

C

  • C 字符串是 char* 类型,是以 '\0' 字符结束的字符数组。
  • C++ 中处理 C 字符串,使用 #include <cstring>

C++

  • string 是类
  • 作为区别,用 cstringC 字符数组
    1
    2
    3
    4
    string s1;   // 默认构造
    string s2 = "c plus plus"; // 用cstring构造
    string s3 = s2; // 用同类对象构造
    string s4 (5, 's'); // 用int,char作为参数构造

访问控制

公有成员

  • 公有成员在客户端可以任意访问。

  • 公有数据成员不需要通过公有函数成员访问,其优点是使用方便,缺点是可能会破坏封装的逻辑一致性

私有成员

  • 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有友元函数可以访问私有成员。

  • 默认情况下,class的所有成员都是私有的。

  • 实际操作中,一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数,保持对象内部状态一致

构造/析构

无参构造函数

  • 类的构造函数是类的一种特殊的成员函数,每次创建类的新对象时执行它完成初始化等逻辑。

  • 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void

  • 如果用户没有自定义构造函数,则编译会自动生成一个默认构造函数

  • C++11 以后建议申明时写含参形式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class date {
    private:
    int year;
    int month;

    public:
    date(int = 2023, int = 1);
    };

    date::date(int y, int m) {
    year = y;
    month = m;
    }

含参构造函数

  • 构造函数也可以带有参数。这样在创建对象时就可使用参数构造对象。

  • 用户一旦定义了构造函数,编译器就不再自动添加默认构造函数。这时调用无参构造函数会报错

  • 构造函数也能使用默认实参。这样可以减少构造函数重载的数量。

析构函数

  • 类的析构函数是类的一种特殊的成员函数,它会在对象被释放前执行

  • 析构函数的名称与类的名称是相同的,只是在前面加 ~ 作为前缀,不会返回任何值也不能带有参数

  • 析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

  • 析构函数不能直接调用

this 指针

注意点

  • C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
  • 当成员函数参数与成员数据重名时,必须使用 this 访问成员数据。
  • 只有动态成员函数才有 this 指针。
  • 友元函数没有 this 指针,因为友元不是类的成员
  • static 成员不能使用 this,应使用 ::
  • -> 是指针取成员运算

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class box { 
public:
// 构造函数定义
Box(double l = 2.0, double w = 2.0, double h = 2.0) {
cout << "Constructor called." << endl;
length = l;
width = w;
height = h;
}
double volume() {
return length * width * height;
}
int compare(Box box) {
return this->Volume() > box.Volume();
}

private:
double length;
double width;
double height;
};
1
2
3
4
5
6
7
8
9
10
int main(void) { 
Box box1(3.3, 1.2, 1.5); // Declare box1
Box box2(8.5, 6.0, 2.0); // Declare box2
if(box1.compare(box2)) {
cout << "box2 is smaller than box1" << endl;
} else {
cout << "box2 is equal to or larger than box1" << endl;
}
return (0);
}

Output:

1
2
3
Constructor called. 
Constructor called.
Box2 is equal to or larger than Box1