目录
一.函数
1.内联函数
2.函数重载
3.哑元函数
二.类和对象
2.1 类的定义
2.2 创建对象
三. 封装(重点)
四. 构造函数 constructor(重点)
4.1 基础使用
4.2 构造初始化列表
4.3 构造函数的调用方式(掌握)
4.4 拷贝构造函数
4.4.1 概念
4.4.2浅拷贝
4.4.3深拷贝
五. 析构函数 destructor(重点)
一.函数
1.内联函数
通常将具有以下性质的函数写为内联函数:
● 代码长度在5行以内
● 不包含复杂的控制语句
● 频繁的被调用
#include <iostream>
using namespace std;
inline void print_string(string str)
{
cout << str << endl;
}
int main()
{
print_string("helloworld");
return 0;
}
后续学习的成员函数默认添加inline修饰。
我们手动添加上inline关键字,将函数声明为内联函数,。但是这个不是我们可以决定的,编译器有自己的判断准则。我们只是非常卑微的给编译器提一个建议,具体是否变为内联函数,还是编译器自己决定的。
2.函数重载
#include <iostream>
using namespace std;
void print_show(int i)
{
cout << "调用int重载:" << i << endl;
}
// 错误 名称相同,参数相同编译器无法区分
//void print_show(int i)
//{
// cout << "调用int重载:" << i << endl;
//}
void print_show(string str)
{
cout << "调用了string重载:" << str << endl;
}
//int print_show(const int i)
//{
// cout << "调用int2重载:" << i << endl;
//}
//void print_show(float i)
//{
// cout << "调用float重载:" << i << endl;
//}
void print_show(double i)
{
cout << "调用double重载:" << i << endl;
}
int main()
{
print_show(2.3);
return 0;
}
3.哑元函数
函数的参数只有类型,没有名称,有这样参数的函数就是哑元函数。
#include <iostream>
using namespace std;
// 哑元函数
void print_show(int)
{
cout << "调用了int哑元函数" << endl;
}
int main()
{
print_show(1);
return 0;
}
作用2:运算符重载中用到
作用3:保持向前兼容
二.类和对象
对象:根据类的描述创造的实体。
2.1 类的定义
类中主要包含两部分:
● 属性 property(成员变量 member value或 数据成员)
在类中存在的变量,用于存储数据,通常是一个名词,例如身高、价格、颜色......
● 行为(成员函数 member function)
可以执行的功能,是类中的函数,通常是一个动词或动词词组,例如:吃饭、运行、关闭......
成员 = 成员变量 + 成员函数
#include <iostream>
using namespace std;
/**
* @brief The MobilePhone class
* 手机类
*/
class MobilePhone // 帕斯卡(大驼峰)命名法:所有单词首字母大写
{
public: // 表示访问不受限
string brand; // 品牌
string model; // 型号
int weight; // 重量
void play_music()
{
cout << "Remedy" << endl;
}
void run_game()
{
cout << "炉石传说" << endl;
}
void communicate()
{
cout << "喂?" << endl;
}
};
2.2 创建对象
C++支持两种对象:
● 栈内存对象
在生命周期(生命周期为所在的{})结束后,自动被回收,调用成员使用.
● 堆内存对象
必须使用new关键字创建对象,使用指针保存对象首地址,使用delete销毁对象,如果创建后没有手动销毁,对象会持续存在(内存泄漏),调用成员使用->(在Qt Creator中按.键自动转为->)
#include <iostream>
using namespace std;
class MobilePhone
{
public:
string brand;
string model;
int weight;
void play_music()
{
cout << "Remedy" << endl;
}
void run_game()
{
cout << "炉石传说" << endl;
}
void communicate()
{
cout << "喂?" << endl;
}
};
int main()
{
// 栈内存对象
MobilePhone mp1;
// 调用成员变量,先赋值,再读取输出
mp1.brand = "苹果";
mp1.model = "16 Pro Max";
mp1.weight = 200;
cout << mp1.brand << endl;
cout << mp1.model << endl;
cout << mp1.weight << endl;
// 调用成员函数
mp1.communicate();
mp1.play_music();
mp1.run_game();
// 堆内存对象
MobilePhone* mp2 = new MobilePhone;
mp2->brand = "华为";
mp2->model = "非凡大师xt1";
mp2->weight = 301;
cout << mp2->brand << endl;
cout << mp2->model << endl;
cout << mp2->weight << endl;
mp2->communicate();
mp2->play_music();
mp2->run_game();
delete mp2; // 销毁mp2
// 不要销毁后还使用
// cout << mp2->brand << endl;
// mp2->communicate();
return 0;
} // mp1销毁
三. 封装(重点)
上面的代码与结构体非常相似,因为结构体就是一种完全开放的类。类通常需要进行封装,封装指的是先将类的一些属性和细节隐藏,再重新提供外部调用接口。
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 私有:被修饰的成员只能在类内访问
string brand; // 读写
string model = "16"; // 只读
int weight; // 只写
public:
string get_brand() // getter:读函数
{
return brand;
}
void set_brand(string b) // setter:写函数
{
brand = b;
}
string get_model()
{
return model;
}
void set_weight(int w)
{
weight = w;
}
};
int main()
{
MobilePhone mp1;
// 调用setter设置属性值
mp1.set_brand("华为");
mp1.set_weight(300);
// 调用getter获取属性值
cout << mp1.get_brand() << endl;
cout << mp1.get_model() << endl;
// TODO 可以试试堆内存对象
return 0;
}
四. 构造函数 constructor(重点)
4.1 基础使用
类中有一种特殊的成员函数,在创建对象时必须调用,这个函数就是构造函数,构造函数的特殊性体现在:
● 不写返回值
● 函数名称必须是类名
● 如果程序员不手动编写构造函数,编译器会自动添加一个无参且函数体为空的构造函数。
构造函数经常用于在创建对象时进行对象属性初始化,构造函数也支持参数默认值和重载。
#include <iostream>
using namespace std;
class MobilePhone
{
private:
string brand;
string model;
int weight;
public:
// 编译器会在程序员不写的情况下添加下面的构造函数
// MobilePhone(){}
MobilePhone()
{
// 给属性赋予初始值
brand = "山寨";
model = "???";
weight = 188;
cout << "构造函数" << endl;
}
MobilePhone(string b,string m,int w)
{
brand = b;
model = m;
weight = w;
cout << "构造函数2" << endl;
}
/**
* @brief show 输出所有属性值
*/
void show()
{
cout << brand << endl;
cout << model << endl;
cout << weight << endl;
}
};
int main()
{
MobilePhone mp1;
mp1.show();
MobilePhone* mp2 = new MobilePhone;
mp2->show();
delete mp2;
MobilePhone mp3("魅族","Lucky08",199);
mp3.show();
MobilePhone* mp4 = new MobilePhone("小米","su7",2100000);
mp4->show();
delete mp4;
cout << "主函数结束" << endl;
return 0;
}
4.2 构造初始化列表
在当前阶段,下面的两种写法是等效。
4.3 构造函数的调用方式(掌握)
构造函数可以显式调用,也可以隐式调用。
显式调用:在创建对象时使用明确的构造函数调用语法,不能被explicit关键字影响。
隐式调用:在创建对象时不使用明确的构造函数调用语法,受到explicit关键字影响
#include <iostream>
using namespace std;
class Student
{
private:
string name;
public:
Student(string n):name(n)
{
cout << "构造函数,name=" << name << endl;
}
string get_name()
{
return name;
}
};
int main()
{
Student s1("张三");
cout << s1.get_name() << endl;
Student* s2 = new Student("李四");
cout << s2->get_name() << endl;
delete s2;
string name = "王五";
Student s3 = name; // 编译器帮忙调用了构造函数
cout << s3.get_name() << endl;
Student s4(name); // 编译器帮忙调用了构造函数
cout << s4.get_name() << endl;
return 0;
}
#include <iostream>
using namespace std;
class Student
{
private:
string name;
public:
// 明确的
explicit Student(string n):name(n)
{
cout << "构造函数,name=" << name << endl;
}
string get_name()
{
return name;
}
};
int main()
{
Student s1("张三"); // 显式
cout << s1.get_name() << endl;
Student* s2 = new Student("李四"); // 显式
cout << s2->get_name() << endl;
delete s2;
string name = "王五";
// Student s3 = name; // 隐式,被explicit屏蔽
// cout << s3.get_name() << endl;
Student s4(name); // 显式
cout << s4.get_name() << endl;
return 0;
}
4.4 拷贝构造函数
4.4.1 概念
如果程序员不手动编写拷贝构造函数,编译器会为每个类增加一个拷贝构造函数。
通过拷贝构造函数创建的新对象,只是属性值与源对象相同,但是会在新的地址空间进行开辟。
#include <iostream>
using namespace std;
class Student
{
private:
string name;
public:
Student(string n):name(n)
{
cout << "构造函数,name=" << name << endl;
}
// 编译器自动添加的拷贝构造函数
// Student(const Student& s)
// {
// name = s.name;
// }
Student(const Student& s)
{
name = s.name;
cout << "拷贝构造函数" << endl;
}
string get_name()
{
cout << &name << endl;
return name;
}
};
int main()
{
Student s1("张三");
cout << s1.get_name() << endl;
Student s2(s1);
cout << s2.get_name() << endl;
return 0;
}
4.4.2浅拷贝
#include <iostream>
#include <string.h> // 头文件
using namespace std;
class Student
{
private:
char* name;
public:
Student(char* n):name(n)
{
cout << "构造函数" << endl;
}
char* get_name()
{
return name;
}
};
int main()
{
char name[20] = "张三";
Student s1(name);
Student s2(s1);
strcpy(name,"李四");
cout << s1.get_name() << endl; // 李四
cout << s2.get_name() << endl; // 李四
return 0;
}
在上面的代码中,在31行修改了26行的字符数组内容,s1和s2隐藏的成员变量的值就变了,且s1和s2的name保存的地址就是26行的name,这都不符合面向对象的特性。
4.4.3深拷贝
#include <iostream>
#include <string.h> // 头文件
using namespace std;
class Student
{
private:
char* name;
public:
Student(char* n):name(new char[20])
{
// 同步两块内存的内容
strcpy(name,n);
cout << "构造函数" << endl;
}
Student(const Student& s)
{
// 同步两块内存的内容
name = new char[20];
strcpy(name,s.name);
cout << "拷贝构造函数" << endl;
}
char* get_name()
{
return name;
}
};
int main()
{
char name[20] = "张三";
Student s1(name);
Student s2(s1);
strcpy(name,"李四");
cout << s1.get_name() << endl; // 张三
cout << s2.get_name() << endl; // 张三
return 0;
}
在上面的深拷贝代码中,new出来的空间并没有delete回收,因此会造成内存泄漏问题。
五. 析构函数 destructor(重点)
析构函数是与构造函数对立的函数,也是一种特殊的成员函数。
构造函数 | 析构函数 |
函数名称为类名 | 函数名称为~类名 |
功能为创建对象并初始化 | 功能为在对象销毁时回收资源 |
在创建对象时调用 | 在对象销毁时自动被调用 |
有参数,支持重载和默认值 | 无参数,不支持重载和默认值 |
程序员不手写析构函数,编译器也会添加一个下面格式的析构函数:
#include <iostream>
using namespace std;
class Dog
{
public:
~Dog()
{
cout << "析构函数" << endl;
}
};
int main()
{
Dog d1;
Dog* d2 = new Dog;
delete d2; // d2输出:析构函数
cout << "主函数结束" << endl;
return 0;
} // d1输出:析构函数