返回项目列表

/note/ICPC笔记

ICPC笔记

当前项目读取 content/notes/ICPC笔记 下的 Markdown 文件。

ICPC笔记

构造函数

构造函数 构造函数是: 创建对象时自动调用的函数 。 1.默认构造函数 没有参数的构造函数 叫默认构造函数 2.带参构造函数 带参构造函数就是创建对象时传入初始值 3. 初始化列表 必须使用初始化列表的情况 const 成员变量 不能写成: 因为 const 变量出生以后就不能改了,所以必须在出生时...

构造函数

构造函数是:创建对象时自动调用的函数

1.默认构造函数

没有参数的构造函数叫默认构造函数

class Student {
private:
    string name;
    int age;

public:
    Student() {
        name = "unknown";
        age = 0;
    }

    void show() const {
        cout << name << " " << age << endl;
    }
};

2.带参构造函数

带参构造函数就是创建对象时传入初始值

class Student {
private:
    string name;
    int age;

public:
    Student(string n, int a) {
        name = n;
        age = a;
    }
};

3. 初始化列表

class Student {
private:
    string name;
    int age;

public:
    Student(string n, int a) : name(n), age(a) {
    }
};

必须使用初始化列表的情况

const 成员变量

class Student {
private:
    const int id;

public:
    Student(int x) : id(x) {
    }
};

不能写成:

class Student {
private:
    const int id;

public:
    Student(int x) {
        id = x; // 错误
    }
};

因为 const 变量出生以后就不能改了,所以必须在出生时初始化。

引用成员变量

class Test {
private:
    int& ref;

public:
    Test(int& x) : ref(x) {
    }
};

引用也必须一开始就绑定。

不能写:

Test(int& x) {
    ref = x; // 错误理解
}

这里不是让 ref 引用 x,而是给 ref 当前绑定的对象赋值。但问题是 ref 还没绑定任何对象。

成员对象没有默认构造函数

比如有一个类:

class Date {
private:
    int year;
    int month;
    int day;

public:
    Date(int y, int m, int d) : year(y), month(m), day(d) {}
};

现在另一个类里面有 Date 成员:

class Student {
private:
    string name;
    Date birthday;

public:
    Student(string n, int y, int m, int d)
        : name(n), birthday(y, m, d) {
    }
};

意思是调用 Date 的构造函数来初始化 birthday

不能这样写:

class Student {
private:
    string name;
    Date birthday;

public:
    Student(string n, int y, int m, int d) {
        name = n;
        birthday = Date(y, m, d); // 通常会出错
    }
};

因为进入构造函数体之前,birthday 已经必须先被构造好。但 Date 没有无参构造函数,所以构造不出来。

初始化顺序

成员变量的初始化顺序不是看初始化列表写的顺序,而是看成员变量声明顺序

比如:

class Test {
private:
    int a;
    int b;

public:
    Test() : b(2), a(1) {
    }
};

虽然初始化列表写的是:

b(2), a(1)

但实际还是:

a 先初始化
b 后初始化

因为声明顺序是:

int a;
int b;

所以建议你永远按照声明顺序写初始化列表:

class Test {
private:
    int a;
    int b;

public:
    Test() : a(1), b(2) {
    }
};

4.拷贝构造函数

拷贝构造函数可以理解成:

用一个已经存在的对象,创建一个新的对象。

它是“出生时复制”,不是“出生后赋值”。

基本写法:

class Student {
private:
    int age;

public:
    Student(int age = 0) : age(age) {}

    // 拷贝构造函数
    Student(const Student& other) : age(other.age) {
        cout << "调用拷贝构造函数" << endl;
    }
};

标准格式是:

类名(const 类名& other)

4.1 为什么要用 &

如果写成这样:

Student(Student other)

这是错误的。

因为调用拷贝构造函数时,要先把实参复制给形参 other

但是复制 other 又需要调用拷贝构造函数。

于是就变成:

调用拷贝构造
  为了传参,又调用拷贝构造
    为了传参,又调用拷贝构造
      ...

无限递归。

所以必须写引用:

Student(Student& other)

这样传参时不会再复制一份。


4.2 为什么要用 const

写成:

Student(const Student& other)

表示:

我只是拿 other 来复制数据,不会修改 other

而且有了 const,它可以接收普通对象,也可以接收 const 对象。

const Student s1(18);
Student s2(s1); // 需要 const Student&

4.3默认拷贝构造函数

即使你不写拷贝构造函数,编译器也会自动生成一个。

比如:

class Point {
private:
    int x;
    int y;

public:
    Point(int x = 0, int y = 0) : x(x), y(y) {}
};

你没写:

Point(const Point& other)

但仍然可以:

Point p1(1, 2);
Point p2 = p1;

编译器默认会帮你做:

p2.x = p1.x;
p2.y = p1.y;

这种叫浅拷贝,也就是成员一个一个复制。