构造函数1(Constructor)
构造函数(也叫构造器), 在对象创建的时候自动调用, 一般用于完成对象的初始化工作.
特点:
(1)函数名与类同名, 无返回值(void都不能写), 可以有参数, 可以重载, 可以有多个构造函数.
(2)一旦自定义了构造函数, 必须用其中一个自定义的构造函数来初始化对象.
注意:
(1)通过malloc分配的对象不会调用构造函数
(2)通过new分配的对象可以调用构造函数, 即在堆空间中的对象可以调用构造函数, 在栈空间的也可以, 在全局区(数据段)的也可以.只有malloc不会调用.因为malloc在C语言就有了, 那时候还没有构造函数.
所以new至少比malloc多做了调用构造函数这件事情.
(3)一个广为流传的, 很多教程\书籍都推崇的错误结论:
默认情况下, 编译器会为每一个类生成空的无参的构造函数.
正确理解: 在某些特定的情况下, 编译器才会为类生成空的无参的构造函数.
构造函数2
构造函数的调用:
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
Person() {
m_age = 0;
cout << "Person()" << endl;
}
Person(int age) {
m_age = age;
cout << "Person(int age)" << endl;
}
};
Person person1(); // 函数声明
Person person1() { // 函数实现
return Person();
}
Person g_person0; // Person();
Person g_person1(); // 全局的函数声明
// 返回值 函数名
Person g_person2(10); // Person(int)
int main()
{
Person person0; // Person()
Person person1(); // 局部的函数声明
Person person2(20); // Person(int)
Person *p0 = new Person; // Person()
Person *p1 = new Person(); // Person()
Person *p2 = new Person(30); // personn(int)
return 0;
}
成员变量的初始化(了解)
如果自定义了构造函数, 除了全局区, 其他内存空间的成员变量默认都不会被初始化, 需要开发人员手动初始化.
如果有很多个成员变量, 如何方便的初始化.
class Person {
int m_age1;
int m_age2;
int m_age3;
int m_age4;
int m_age5;
Person() {
memset(this, 0, sizeof(Person));
// 从person对象的地址开始, 它的所有字节都清0
}
};
析构函数(Destructor)
析构函数(也叫析构器), 在对象销毁(对象的内存被回收的时候)的时候自动调用, 一般用于完成对象的清理工作.
特点:
(1)函数名以~开头, 与类同名, 无返回值(void都不能写), 无参, 不可以重载, 有且仅有一个析构函数.
注意:
(1)通过malloc分配的对象free的时候不会调用析构函数.
(2)构造函数, 析构函数要声明为public, 才能被外界正常使用.所以构造函数和析构函数必须声明为public, 否则会报错.
内存管理
声明和实现分离
命名空间
继承
继承, 可以让子类拥有父类的所有成员(变量\函数)
成员访问权限
成员访问权限, 继承方式有3种
public : 公共的, 任何地方都可以访问(struct默认)
protected: 子类内部, 当前类内部可以访问
private: 私有的, 只有当前类内部可以访问(class默认)
结论:
子类内部访问父类成员的权限, 是以下2项中权限最小的那个
1.成员本身的访问权限
2.上一级父类的继承方式
(1)所以一般都写public继承, 因为public继承可以将父类原有的访问权限继承下来.
(2)访问权限不影响对象的内存布局.
初始化列表1
特点:
(1)一种便捷的初始化成员变量的方式
(2)只能用在构造函数中
(3)初始化顺序只跟成员变量的声明顺序有关
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
/*Person(int age, int height) {
m_age = age;
m_height = height;
}*/
// 语法糖
Person(int age, int height) : m_age(age), m_height(height) {
}
// 这个构造函数与上面的构造函数等价
};
int main()
{
Person person(18, 180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
初始化列表与默认参数配合使用
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
Person(int age = 0, int height = 0) : m_age(age), m_height(height) {
}
// 一个构造函数相当于写了3个构造函数
};
int main()
{
Person person1;
Person person2(19);
Person person3(20, 180);
return 0;
}
如果函数声明和实现是分离的
初始化列表只能写在函数的实现中
默认参数只能写在函数声明中
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
Person(int age = 0, int height = 0);
};
Person::Person(int age, int height) : m_age(age), m_height(height) {
}
int main()
{
Person person;
return 0;
}
构造函数的互相调用
结论:构造函数调用构造函数要在初始化列表里面写.
例如:
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
Person() : Person(0, 0) {}
Person(int age, int height) : m_age(age), m_height(height) {}
};
int main()
{
Person person;
return 0;
}
注意:下面的写法是错误的, 初始化的是一个临时对象.
class Person {
int m_age;
int m_height;
Person() {
Person(0, 0);
}
Person(int age, int height) : m_age(age), m_height(height) {}
};
初始化列表3-父类的构造函数
(1)子类的构造函数默认会调用父类的无参构造函数
(2)如果子类的构造函数显式地调用了父类的有参构造函数, 就不会再去默认调用父类的无参构造函数
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
Person() {
cout << "Person::Person()" << endl;
}
Person(int age) {
cout << "Person::Person(int age)" << endl;
}
};
class Student : public Person {
public:
int m_no;
Student() : Person(10) {
cout << "Student::Student()" << endl;
}
};
int main()
{
Student student;
return 0;
}
(3)如果父类缺少无参构造函数(但有有参构造函数), 子类的构造函数必须显式调用父类的有参构造函数.不然会报错. 因为子类的构造函数默认会调用父类无参的构造函数, 如果父类缺少无参的构造函数, 就报错.
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
Person(int age) {
cout << "Person::Person(int age)" << endl;
}
};
class Student : public Person {
public:
int m_no;
Student() : Person(10) {
cout << "Student::Student()" << endl;
}
//父类缺少无参的构造函数, 子类的构造函数
//必须显式的调用父类的有参构造函数.
};
int main()
{
Student student;
return 0;
}
(4)如果父类什么构造函数都没有, 那子类的构造函数就不调用了.
(5)价值:
#include <iostream>
using namespace std;
class Person {
private:
int m_age;
public:
Person(int age) : m_age(age){ }
int getAge() {
return m_age;
}
};
class Student : public Person {
private:
int m_no;
public:
Student(int age, int no) : Person(age), m_no(no) { }
int getNo() {
return m_no;
}
};
int main()
{
Student student(10, 20);
cout << student.getAge() << '\n' <<
student.getNo() << endl;
return 0;
}