C++类和对象的概念及定义
一. 什么是面向对象?
1. 面向对象的定义
面向对象是软件开发的一种方法,它的主要思想就是:把整个世界看做是具有行为活动各种对象组成的。把数据以及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象其共性形成类。所以,对象也就可以看做是类的具体实例,我们就可以把类看做建房子的图纸,对象就可以看做建出来的房子。将对象作为程序的基本单元,将程序和数据封装在其中,以提高软件的重用性、灵活性、扩展性。
要注意的是:C++不是纯面向对象的语言,而是基于面向对象的语言。因为C++包含C的部分,C是面向过程的语言。
2. 面向对象的三大特性
封装、继承、多态
(1)封装性
把数据和方法封装到一起,其中方法一般用函数实现。数据表示类的属性,函数则表示类的行为。而类的访问限定符public、private、protected等限定了访问的权限,达到封装的目的。
(2)继承性
通过继承,一个对象可以获得另一个对象的属性(包括函数),并且可以向其中加入一些自己的特征。通过继承得到的新类我们叫做“子类”或者“派生类”,被继承的类叫做“父类”或“基类”。
(3)多态性
多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
二. 类的声明及定义
1. 关键字class/struct
类可以通过class/struct去声明。但是对于class声明的类,它的默认属性为私有的(private);但对于struct声明的类,它的默认属性是公有的(public)。
编写struct测试代码如下:
#include <iostream>
using namespace std;
struct Person1
{
int b;
void Display()
{
cout<<b<<endl;
}
};
int main()
{
Person1 p1;
p1.b = 6;
p1.Display();
return 0;
}
运行结果如下:
编写class测试代码如下:
#include <iostream>
using namespace std;
class Person
{
int a;
void Display()
{
cout<<a<<endl;
}
};
int main()
{
Person p;
p.a = 5;
p.Display();
return 0;
}
运行结果如下:
可以看到未指明访问限定符时,class声明的类中都是默认私有的属性,在类外无法访问私有变量及私有函数。
2. C++的数据类型
3. 三种访问限定符
(1)public的成员可以从类外直接访问,private和protected属性的成员不可以从类外直接访问;
(2)每个访问限定符可以在类内出现多次,它的作用域是从该限定符开始到下一个限定符出现或到类体结束;
(3)类体中若没有访问限定符,根据声明的关键字是class/struct决定默认属性是private/public;
(4)类的访问限定符体现了面向对象的封装性。
4. 作用域
(1)每个类都定义了自己的作用域。类的成员(成员函数、成员变量)都在类的作用域中,成员函数可以任意访问成员变量和其他成员函数;
(2)对象可以通过“.”直接访问类的公有成员,指向对象的指针也可以通过“->”直接访问对象的公有成员;
(3)在类外定义成员,需要使用“::”作用域解析符指明成员属于哪个类域。
5. 定义一个简单的类
(1)类内定义成员变量
#include <iostream>
using namespace std;
class Person
{
public:
void Display()//成员函数
{
cout<<_name<<"-"<<_sex<<"-"<<_age<<endl;
}
public://成员变量
char* _name;
char* _sex;
int _age;
};
int main()
{
//将类实例化为一个对象
Person p;
p._name = "YoungJack";
p._sex = "男";
p._age = 25;
p.Display();
//可以通过指针修改
Person* ptr = &p;
ptr->_name = "满舒克";
ptr->_sex = "男";
ptr->_age = 28;
ptr->Display();
return 0;
}
(2)类外定义成员函数
#include <iostream>
using namespace std;
class Person
{
public:
void Display();//成员函数
public://成员变量
char* _name;
char* _sex;
int _age;
};
void Person::Display()
{
cout<<_name<<"-"<<_sex<<"-"<<_age<<endl;
}
int main()
{
//将类实例化为一个对象
Person p;
p._name = "YoungJack";
p._sex = "男";
p._age = 25;
p.Display();
//可以通过指针修改
Person* ptr = &p;
ptr->_name = "满舒克";
ptr->_sex = "男";
ptr->_age = 28;
ptr->Display();
return 0;
}
两种方式的运行结果都一样,如下:
三. 类的大小
1. 类的大小
每个对象的大小为类中所有成员变量的大小之和并且遵循内存对齐原则。这里成员函数(不包括虚函数)并不计入对象的大小之中。因为,类的成员函数在被实例化的对象调用时,它是为所有对象公用,通过this指针和类的实例相关联,在编译时由编译器直接调用,与普通函数并没有什么区别,代码编译之后根本不在类的实例中,所以不占空间。
2. 为什么要内存对齐?
具体原理如下图所示:
3. 类的大小的计算
对于遵循内存对齐的原则,要遵循的规则在之前的结构体相关博客已经介绍过了,这里不再介绍,直接看代码:
#include <iostream>
using namespace std;
class A
{
char ch;
double d;
};
class B
{
char ch;
A a;
char ch2;
};
int main()
{
int size1 = sizeof(A);
int size2 = sizeof(B);
cout<<size1<<endl;
cout<<size2<<endl;
return 0;
}
运行结果为:
(1)对于类A,分析过程如下:
1)首先ch占一个字节;
2)Linux下的默认对齐数为4,所以要空3字节,对齐到4的1倍处,此时共4字节;
3)然后d占8字节,刚好在4的整数倍处,此时共12字节;
4)12字节刚好是最大对齐数4的整数倍,所以类A的大小为12字节。
(2)对于类B,分析过程如下:
1)首先ch占一个字节;
2)Linux下的默认对齐数为4,所以要空3字节,对齐到4的1倍处,此时共4字节;
3)然后类A的对象大小为12字节,刚好是4的整数倍,此时共16字节;
4)然后ch2占1字节,再补3字节的空,使其对齐在4的整数倍处,此时共20字节;
4)20字节刚好是最大对齐数4的整数倍,所以类B的大小为20字节。
4. 空类的大小
空类的大小为1字节,验证如下:
#include <iostream>
using namespace std;
class A
{
};
int main()
{
cout<<sizeof(A)<<endl;
return 0;
}
可能大家会有疑问,空类的大小不应该是0字节吗?怎么会是1字节呢?它其实是起到一个“占位”的作用,用来表示该类存在过。