类和对象
定义:具有相同性质的对象,可以抽象的理解为一类
例如:
人类:张三、李四、王五都是人,他们都有相同的性质,比如:姓名、年龄、性别、身高、体重等。
车类:宝马、奔驰、奥迪都是车,他们都有相同的性质,比如:品牌、颜色、速度、价格等。
封装
封装的意义
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
就是在写代码的时候,将属性和行为放在一起,放在一个类中,然后通过权限控制来控制属性和行为的访问权限
语法:
1 2 3 4 5
| class 类名{ 访问权限: 属性/成员属性 行为/成员函数 }
|
封装的意义①:
示例1:求圆的周长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <iostream> using namespace std;
const double PI = 3.14;
class Circle{
public:
int m_r;
double calculateZC(){ return 2 * PI * m_r; } };
int main(){
Circle c1;
c1.m_r = 10;
cout << "圆的周长为:" << c1.calculateZC() << endl; return 0; }
|
输出:
封装的意义②:
访问权限有三种:
- public:公共权限 类内可访问 类外可访问
- protected:保护权限 类内可访问 类外不可访问 子类可访问
- private:私有权限 类内可访问 类外不可访问 子类不可访问
示例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <iostream> using namespace std;
class Person{
public:
string m_name = "李四";
void showName(){ cout << "姓名为:" << m_name << endl; }
protected:
string m_age = 16;
void showAge(){ cout << "年龄为:" << m_age << endl; }
private:
string m_height = 190;
void showHeight(){ cout << "身高为:" << m_height << endl; } }; int main(){
Person p1;
p1.m_name = "张三"; p1.m_age = "18"; p1.m_height = "180cm";
p1.showName();
p1.showAge();
p1.showHeight(); return 0; }
|
输出:
在类外设置的值
姓名:张三
年龄:18
身高:180cm
但因为访问权限不同,所以输出的和类外规定的有差异
struct和class区别
在C++中,struct和class的区别只有 默认权限 的不同:
- struct:默认权限为public
- class:默认权限为private
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> using namespace std;
struct Person1{ int m_A; };
class Person2{ int m_A; }; int main(){ Person1 p1; p1.m_A = 10; cout << "p1.m_A = " << p1.m_A << endl; Person2 p2; p2.m_A = 10; cout << "p2.m_A = " << p2.m_A << endl; return 0; }
|
输出:
成员属性设置为私有
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
核心:在类外调用公共部分,公共部分在类内对私有部分进行更改
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <iostream> using namespace std;
class Person{ public: void setName(string name){ m_name = name; } string getName(){ return m_name; } void setAge(int age){ if(age < 0 || age > 150){ cout << "年龄不合法!" << endl; return; } } int getHeight(){ return m_height; } private: string m_name; int m_age; int m_height = 180; }; int main(){ Person p1; p1.setName("张三"); cout << "姓名为:" << p1.getName() << endl; p1.setAge(18); return 0; }
|
输出:
对象的初始化和清理
c++中,每个对象都会有初始设置以及对象销毁前的清理数据的设置
构造函数和析构函数
c++中对象的初始化和清理,有两种方式:
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法:
1.构造函数没有返回值也不写void
2.函数名称与类名相同
3.构造函数可以有参数,因此可以发生重载
4.系统会自动调用构造函数,无需手动调用,而且只会调用一次
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> using namespace std;
class Person{ public: Person(){ cout << "构造函数调用" << endl; } }; int main(){ Person p1; return 0; }
|
输出:
析构函数语法:
1.析构函数没有返回值也不写void
2.函数名称与类名相同,在名称前加上~
3.析构函数不可以有参数,因此不可以发生重载
4.对象在销毁前,系统会自动调用析构函数,无需手动调用,而且只会调用一次
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> using namespace std;
class Person{ public: ~Person(){ cout << "析构函数调用" << endl; } }; int main(){ Person p1; return 0; }
|
输出:
二合一示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> using namespace std;
class Person{ public: Person(){ cout << "构造函数调用" << endl; } ~Person(){ cout << "析构函数调用" << endl; } }; int main(){ Person p1; return 0; }
|
输出:
1 2 3
| 构造函数调用 析构函数调用 请按任意键继续. . .
|
在Person类的创建中,调用了构造函数,而在程序结束,类销毁时,调用了析构函数。
假如我们把 main 函数部分稍作更改,可能会看的更清晰
1 2 3 4 5
| int main(){ Person p1; system("pause"); return 0; }
|
输出:
这个时候我们点击任意键,程序会释放内存,调用析构函数
1 2 3
| 构造函数调用 请按任意键继续... 析构函数调用
|
可以看到,在程序暂停时,析构函数并没有被调用
这是因为,程序在暂停时,对象还没被销毁,所以析构函数没有被调用。
构造函数的分类及调用
两种分类方式:
- 按参数分为:有参构造和无参构造
- 按类型分为:普通构造和拷贝构造
三种调用方式:
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <iostream> using namespace std;
class Person{ public: Person(){ cout << "无参构造函数调用" << endl; } Person(int a){ cout << "有参构造函数调用" << endl; } Person(const Person &p){ cout << "拷贝构造函数调用" << endl; }
~Person(){ cout << "析构函数调用" << endl; } }
viod test01(){
}
int main(){ test01(); return 0; }
|
现在我们分别从三种调用方式来分析
- 括号法
1 2 3 4 5 6 7 8
| viod test01(){ Person p1; Person p2(10); Person p3(p2);
cout << p2.age << endl; cout << p3.age << endl; }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 无参构造函数调用 有参构造函数调用 拷贝构造函数调用
10 10
析构函数调用 析构函数调用 析构函数调用
|
注意:
- 调用无参构造函数时,不要加括号,会被系统认为是函数声明而不调用构造函数
- 显示法
1 2 3 4 5
| viod test01(){ Person p1; Person p2 = Person(10); Person p3 = Person(p2); }
|
输出:
1 2 3 4 5 6 7
| 无参构造函数调用 有参构造函数调用 拷贝构造函数调用
析构函数调用 析构函数调用 析构函数调用
|
注意:
- Person(10) 是一个匿名对象,当前行结束后,系统会立即释放这个对象再进行下一步!!!
例如:
1 2 3 4
| viod test01(){ Person(10); cout << "hello" << endl; }
|
输出:
- 隐式转换法
1 2 3 4 5
| viod test01(){ Person p1; Person p2 = 10; Person p3 = p2; }
|
输出:
1 2 3 4 5 6 7
| 无参构造函数调用 有参构造函数调用 拷贝构造函数调用
析构函数调用 析构函数调用 析构函数调用
|
拷贝构造函数调用时机
C++中拷贝构造函数调用时机通常有三种情况:
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <iostream> using namespace std;
class Person{ public: Person(){ cout << "无参构造函数调用" << endl; } Person(int a){ cout << "有参构造函数调用" << endl; } Person(const Person &p){ cout << "拷贝构造函数调用" << endl; } ~Person(){ cout << "析构函数调用" << endl; } }
void test01(){ Person p1(20); Person p2(p1); }
void doWork(Person p){ } void test02(){ Person p; doWork(p); }
Person doWork2(){ Person p1; return p1; } void test03(){ Person p = doWork2(); } int main(){ test01(); test02(); test03(); }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 有参构造函数调用 拷贝构造函数调用 析构函数调用 析构函数调用
无参构造函数调用 拷贝构造函数调用 析构函数调用 析构函数调用
无参构造函数调用 拷贝构造函数调用 析构函数调用 析构函数调用 析构函数调用
|
可以看到,在三种情况下,都调用了拷贝构造函数。
这是因为,在创建新对象时,会调用拷贝构造函数来初始化新对象。
友元
全局函数做友元
- 全局函数做友元的目的是为了让全局函数能够访问类中的私有成员
- 友元的声明可以放在类的任何地方,不受访问权限的限制
- 语法:
1 2 3
| class 类名{ friend void 全局函数名(参数列表); }
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> using namespace std;
class Person{ public: Person(){ cout << "无参构造函数调用" << endl; } }
void goodFriend(Person &p){ cout << "全局函数goodFriend" << endl; } int main(){ Person p1; goodFriend(p1); return 0; }
|
输出:
1 2
| 无参构造函数调用 全局函数goodFriend
|
可以看到,全局函数goodFriend成功的访问了类中的私有成员。
类做友元
- 类做友元的目的是为了让类中的所有成员函数都能够访问另一个类中的私有成员
- 友元的声明可以放在类的任何地方,不受访问权限的限制
- 语法:
1 2 3
| class 类名{ friend class 类名; }
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> using namespace std;
class Person{ public: Person(){ cout << "无参构造函数调用" << endl; } void goodFriend(){ cout << "成员函数goodFriend" << endl; } }
class Building{ friend class Person; } int main(){ Building b1; Person p1; p1.goodFriend(); return 0; }
|
输出:
1 2 3
| 无参构造函数调用 无参构造函数调用 成员函数goodFriend
|
可以看到,类中的成员函数goodFriend成功的访问了类中的私有成员。
、】【