.1构造函数和析构函数
若人为不实现,编译器会自动实现但函数内为空
构造函数:创建对象时为对象的成员属性赋值 类名(){}
- 没有返回值
- 可以有参数,可以重载
- 系统自动调用,不用手动调用,并且只调用一次
析构函数:对象销毁前清理工作 ~类名(){}
- 没有返回值
- 不可以有参数,无法重载
- 系统自动调用,不用手动调用,并且只调用一次
.2构造函数的分类及调用
按参数分类 无参构造(默认构造)和有参构造
按类型分类 普通构造和拷贝构造函数
class test {
public:
//无参构造
test() {
cout <<"test的无参构造函数调用"<<endl;
}
//有参构造
test(int a) {
cout <<"test的有参构造函数调用"<<endl;
}
//拷贝构造
test(const test &p){
//将p的成员属性拷贝到当前对象上
cout <<"test的拷贝构造函数调用"<<endl;
}
};
调用:
- 括号法:
test p1; //**
test p2(10);
test p3(p2);
注意:调用默认构造函数时不要加()。因为编译器会认为这是一个函数声明
- 显示法:
test p1;
test p2=test(10);
test p3=test(p2);
单独出现的test(10);
称为匿名对象 特点:当前行执行结束,系统会立即回收匿名对象
注意:不要利用拷贝构造函数 初始化匿名对象。因为编译器会认为重定义对象声明test(p3);
×
- 隐式转换法:
test p2=10; //有参构造
test p3=p2; //拷贝构造
.3拷贝构造函数的调用时机
- 使用一个已经创建完的对象来初始化另一个
Person p1;
Person p2(p1);
- 值传递的方式给函数参数传值
void dotest(Person p) {
//在该函数内调用拷贝构造函数,相当于一个副本
//不会更改函数外p的 成员属性
}
void test() {
Person p;
dotest(p);
}
- 值方式返回局部对象
Person dotest2() {
Person p1;
return p1; //返回时根据p1创建一个新的对象
}
.4构造函数调用规则
默认情况,编译器提供
- 默认构造函数(无参,函数体空)
- 默认析构函数(无参,函数体空)
- 默认拷贝构造函数,仅对属性进行值拷贝
- 赋值运算符operator= 对属性进行拷贝 (见.5赋值运算符重载)
若自己定义有参构造函数,编译器不再提供①默认构造,但提供拷贝构造
若自己定义拷贝构造,编译器不再提供其他构造函数
.5深拷贝与浅拷贝
浅拷贝:简单的赋值,编译器自动实现的拷贝构造函数
深拷贝:堆区重新申请一片空间,进行拷贝构造操作
浅拷贝会造成堆区内存重复释放,因为仅仅赋值
#include <iostream>
using namespace std;
class Per {
public:
Per() {
cout << "Pe的默认构造函数调用" << endl;
}
Per(int age, int height) {
m_age = age;
m_height = new int(height);
cout << "Pe的有参构造函数调用" << endl;
}
Per(const Per& p) {
cout << "Pe的拷贝构造函数调用" << endl;
m_age = p.m_age;
//m_height=p.m_height; 编译器默认实现这行代码 浅拷贝
m_height = new int(*p.m_height); //深拷贝 在堆区开辟另一片区域
}
~Per() {
//堆区有内存时,需要在析构函数中把内存释放
if (m_height != NULL) {
delete m_height; //释放掉指针开辟的空间
}
cout << "Pe的析构函数调用" << endl;
}
int m_age;
int* m_height; //指针在堆区开辟新的空间
};
void test1() {
Per p1(12, 150); //析构函数第一次释放掉 p1的指针m_height指向的内存空间
Per p2(p1); //析构函数又一次释放p2的指针指向的内存空间,重复释放
}
int main() {
test1();
system("pause");
return 0;
}
.6初始化列表
语法:构造函数():属性1(值1),属性(值2){}
class Per {
public:
Per(int a,int b,int c):m_a(a),m_b(b),m_c(c){} //初始化列表
private:
int m_a;
int m_b;
int m_c;
};
需要初始化时Per(10,20,30);即可
.7类对象作为成员属性
对象成员:类中成员是另一个类的对象
当其他类对象作为本类成员时,先构造类对象,在构造自身,析构顺序相反
class pen {
public:
pen(string name) {
m_pname = name;
cout << "pen的构造函数调用" << endl;
}
string m_pname;
};
class Person {
public:
//m_pen (name) 相当于采用隐式转换法 Pen m_pname=name
Person (int sex,string name) :m_sex(sex),m_pen(name) //初始化列表
{
cout << "person的构造函数调用" << endl;
}
pen m_pen; //笔名
int m_sex; //性别
};
.8静态成员
静态成员变量:
- 所有对象共享同一份数据,指向同一数据
- 编译阶段分配内存
- 类内声明,类外初始化
数据类型 类名::成员变量名=
class Person {
public:
static int m_sex; //性别
};
int Person::m_sex = 1; //类外初始化
void test1() {
//静态成员的访问方式
//1,通过对象访问
//Person p
//cout <<p.m_sex<<endl;
//2.通过类名进行访问
cout << Person::m_sex << endl; //但私有权限下的无法通过类名访问到
}
静态成员函数:
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量
- 私有权限类外无法访问静态成员函数,只能通过对象访问
class Person {
public:
static void fun() {
m_sex = 1; //可以访问静态成员变量
//b = 20; //无法访问非静态,无法区分是哪个对象的成员变量
}
static int m_sex; //性别
int b;
};
int Person::m_sex = 1; //类外初始化