目录
类的概念
类的构成与设计
类的基本使用
方法一 .的使用
方法二 使用指针
构造函数
构造函数的作用
构造函数的特点
构造函数的种类
默认构造函数
合成的默认函数
自定义的默认构造函数
自定义的重载构造函数
拷贝构造函数
手动定义的拷贝构造函数
合成的拷贝构造函数
浅拷贝和深拷贝
什么时候调用拷贝构造函数
赋值构造函数
析构函数
类是一个抽象的概念
类是看不见,摸不着的,是一个纯粹的概念
类是一种特殊的数据类型,不是一个具体的数据
Attention:类,和基本数据不同(char/int/short/long/long long/float/double)
eg:我们定义一个人类
#include
#include
#include
using namespace std;
class Human{
public://这是公用的 对外的
void eat(){}//方法 ,成员函数
void sleep(){}
void paly(){}
void work(){}
string getName();
int getAge();
int getSalary();
private://私有的 数据 不能直接从外部访问
string name;
int age = 18;//类内初始值
int salary;
};
再随便给出这些方法的实现
int Human::getAge(){
return age;
}
int Human::getSalary(){
return salary;
}
string Human::getName(){
return name;
}
什么是对象?
对象,是一个特定的”类“的具体实例
对象和普通变量有什么区别?
一般地,一个对象,就是一个特别的变量,但是拥有丰富的功能用法
通过对象,能调用这个对象的public方法;且多个对象都有自己的数据,彼此无关;
-> 的使用(类似C语言的结构体用法)p需要初始化到h1才能使用。
- 在创建一个新的对象时,自动调用的函数,用来进行初始化的工作。
- 对这个对象内部的成员进行初始化
1)自动调用(在创建新对象时,自动调用)
2)构造函数的函数名,和类名相同
3)构造函数没有返回类型
4)可以有多个构造函数(即函数重载形式)
默认构造函数
自定义的构造函数
拷贝构造函数
赋值构造函数
没有参数的构造函数,称为默认构造函数
没有手动定义默认构造函数时,编译器自动为这个类定义一个构造函数。
1)如果数据成员使用了“类内初始值”,就使用这个值来初始化数据成员。【C++11】
2)否则,就使用默认初始化(实际上,不做任何初始化)
实例
int main(void) {
Human h1; // 使用合成的默认初始化构造函数
cout << "年龄: " << h1.getAge() << endl; //使用了类内初始值
cout << "薪资:" << h1.getSalary() << endl; //没有类内初始值
system("pause");
return 0;
}
发现问题:薪资salary是一个很大的负值。解决方法:使用手动定义的构造函数进行初始化
只要手动定义了任何一个构造函数,编译器就不会生成“合成的默认构造函数”
一般情况下,都应该定义自己的构造函数,不要使用“合成的默认构造函数”
常称为默认构造函数
在Human中定义构造函数 实现构造函数;
class Human {
public://这是公用的 对外的
Human();
…………
};
Human::Human() {
cout << "调用默认的构造函数" << endl;
name = "无名氏";
age = 25;
salary = 30000;
}
说明:如果某数据成员使用类内初始值
同时又在构造函数中进行了初始化 那么以构造函数中的初始化为准。
相当于构造函数中的初始化,会覆盖对应的类内初始值(类内初始值age = 18 输出age = 25)
使用this指针访问类Human 下private下的数据
在class下定义Human的重载函数
class Human {
public://这是公用的 对外的
Human(int age,int salary);
…………
};
Human重载的实现
Human::Human(int age, int salary) {
cout << "调用自定义的构造函数" << endl;
this->age = age;
this->salary = salary;
name = "Luciferau";
}
Human(const Human& man);//自定义的拷贝构造函数
Human::Human(const Human& man) {
age = man.age;
salary = man.salary;
//使用深拷贝
addr = new char[ADDR_LEN];
strcpy_s(addr, ADDR_LEN, man.addr);
}
class Human{
void description();
…………
}
void Human::description(){
cout << "name" << name << endl;
cout << "age" << age<
都给addr分配内存 进行深拷贝
Human::Human(int age, int salary) {
cout << "调用自定义的构造函数" << endl;
this->age = age;
this->salary = salary;
name = "Luciferau";
addr = new char[ADDR_LEN];
strcpy_s(addr, ADDR_LEN, "China");
}
Human::Human() {
cout << "调用默认的构造函数" << endl;
name = "Luciferau";
age = 25;
salary = 30000;
addr = new char[ADDR_LEN];
strcpy_s(addr, ADDR_LEN, "China");
}
先定义一个更改地址的函数: setaddr();
void Human::setaddr(const char* newAddr) {
if (newAddr) {
return;
}
strcpy_s(addr, ADDR_LEN, newAddr);
}
注释我们写的拷贝构造函数,让程序自动一个默认的拷贝构造函数
编写一个获取地址的函数
const char* Human::getAddr() {
return addr;
}
编写main函数
我们可以看到h2的addr也变成了长沙。因为合成的拷贝构造函数使用的是浅拷贝,我们需要自定义拷贝构造函数进行深拷贝。
合成的拷贝构造函数的缺点: 使用“浅拷贝”
详见我的这篇文章
调用函数时,实参是对象,形参不是引用类型,如果函数的形参是引用类型,就不会调用拷贝构造函数
函数的返回类型是类,而且不是引用类型
对象数组的初始化列表中,使用对象。
调用形参是对象就会调用一次拷贝构造函数,使用指针和引用就可以避免此问题。形参是函数定义中的参数
形参指的是函数定义中的参数即为括号中的,实参指的是使用函数时调用的参数即为实参。
1.当实参是对象,形参不是引用类型
Human h1(25, 35000);//使用自定义的默认构造函数
Human h2(28, 40000);
getBetterman(h1,h2);
Human getBetterman(const Human man1,const Human man2) {
if (man1.getSalary() > man2.getSalary()) {
return man1;
}
else {
return man2;
}
}
//调用二次拷贝构造函数Human h1,Human h2,这里是实参为对象而形参不是引用
//在调用一次拷贝构造函数 return man1 or man2 创建一个临时对象
代码优化
const Human& getBetterman(const Human &man1,const Human &man2) {
if (man1.getSalary() > man2.getSalary()) {
return man1;
}
else {
return man2;
}
}
//这样一次都不会调用拷贝构造函数
初始化列表(初始化列表中使用对象)
Human f1, f2, f3, f4;
Human F4[4] = { f1, f2, f3, f4};
//调用拷贝构造函数
什么时候会调用赋值构造函数呢 ??
Human f1, f2;
f1 = f2;//自动调用赋值构造函数!! 如果不定义赋值构造函数,编译器会生成一个合成的构造函数
当已经定义,定义后赋值给f1的时候会调用复制构造函数
一下情况则不会调用拷贝构造函数
Human f1;
Human f2 = f1; //这样f1 f2,调用的是拷贝构造函数 定义时候直接进行初始化
Attention: 合成的拷贝构造函数 都是浅拷贝(位拷贝)。
需要进行深拷贝
int main(void) {
Human f1, f2;
f2 = f1;
f1.description();
f2.description();
cout << "-----------------" << endl;
f1.setAddr("新加坡");
f1.description();
f2.description();
system("pause");
return 0;
}
Human& Human::operator=(const Human& man) {
cout << "调用" << __FUNCTION__ << endl;
if (this == &man) {
return *this; //检测是不是对自己赋值:比如 h1 = h1;
}
age = man.age;
salary = man.salary;
/*addr = new char[ADDR_LEN];
这是进行深拷贝 分配内存的代码
strcpy_s(addr, ADDR_LEN, man.addr);*/
addr = man.addr;//浅拷贝
//反对对象的本身引用 以便为了做链式处理 f1 = f2 = f3
return *this; //加*为对象本身 不加*是一个指针
}
//拷贝前先初始化addr 不然会将一个无效参数传递给一个无效参数
Human::Human() {
name = "无名氏";
age = 18;
salary = 30000;
addr = new char[ADDR_LEN];
strcpy_s(addr, ADDR_LEN, "China");
}
如果有必要请是释放自己资源
delete addr; addr = new char[ADDR_LEN];//重新申请一块内存
作用:对象销毁前,坐清理工作(释放内存等),比如:如果在构造函数中,使用new分配了内存,就需在析构函数中用delete释放。
函数名:~类型
没有返回值,没有参数,最多只能有一个析构函数
使用方法:
不能主动调用。
对象销毁时,自动调用。
如果不定义,编译器会自动生成一个析构函数(什么也不做)
Human::~Human() {
delete addr;
}