=======================================Begin===========================================
类: 对某类事物特征的抽象。 关键子: class 。"抽象", 这个专业术语可能有些难以难以理解,个人体会,抽象是一个过程,
即将某类事物的特征进行提取, 划分,归类,找出一些共同的特征,将这些共同的特征进行概念话。总之概括起来就是一句
话,“由具体的事物---->非具体事物的过程叫做抽象"。结合下列例子体会 class中抽象这一特征。
菠萝,苹果,香蕉,桃,西瓜,它们见各有特色,它们也有共同点,它们都是水果,都具有水果的特性,例如可以食用,
富含维生素等。社会各种形形色色的人,各有千秋,但他们都是人。从菠萝,苹果,香蕉,桃,西瓜推导出水果的这一
过程叫做抽象。
==========================================================================================
类本身不占用内存空间,但是类对象实实在在占用内存空间,那么类对象是如何在内存中存储的呢?
本人采用的平台Ubuntu16.04, x86_64架构, 8G内存, Linux kernel 4.4, gcc/g++ 5.3,
(1)没有虚函数的类对象在内存的存储方式
#include<iostream>
#include<cstring>
using namespace std;
class Student{
public:
char* _sname;
int _sid;
protected:
int _sage;
private:
int _score;
public:
Student(int, char*, int, int);
~Student();
void Display();
void ClassType();
void FunctionStudent();
};
Student::Student(int sid, char* sname, int sage, int score)
:_sid(sid), _sage(sage), _score(score){
int len = strlen(sname)+1;
this->_sname = new char[len];
strcpy(this->_sname, sname);
}
Student::~Student(){
delete[] this->_sname;
}
void Student::Display(){
cout << "SID = "<<this->_sid<<endl;
cout << "SName = "<<this->_sname<<endl;
cout << "SAge = "<<this->_sage<<endl;
cout << "Score = "<<this->_score<<endl;
}
void Student::ClassType(){
cout << "This is Student Class"<<endl;
}
void Student::FunctionStudent(){
cout << "Student::FunctionStudent()"<<endl;
}
int main(void){
Student stu(100, (char*)"river", 22, 99);
Student* pstu = &stu;
//stu.Display();
pstu->Display();
//cout << cout.setf(ios::hex) << pstu << endl;
//cout << cout.setf(ios::hex) << &stu <<endl;
cout << "sizeof(int) = " << sizeof(int)<<" " << "sizeof(char*) = "<< sizeof(char*) << endl;
cout << "sizeof(stu._sname) = "<< sizeof(stu._sname) << endl;
//cout << "sizeof(Student) = " << sizeof(Student) << endl;
cout << "sizeof(Student*) = " << sizeof(pstu) << endl;
cout << "sizeof(Student) = " << sizeof(stu) << endl;
return 0;
}
分析: Student对象stu的数据在内存存储是按照数据成员在类中的声明的顺序的进行存储的, 第一个数据成员变量
是_sname, char*,占8B,第二个数据成员是_sid, int, 占4B, 0x00000064, 第三数据成员是_sage, int, 占4B, 0x00000016,
第四数据成员是_score, int, 占4B, 0x00000063。此时 sizeof(Student) = 8+4+4+4= 20 != 24,
注意: 数据对齐,为了满足硬件对数据读取的需要,对数据进行对齐,即类的大小为长度最大数据元素的整数数倍。
eg: Student类中长度最大的数据元素为_sname, 8Byte。所以sizeof(Student) >= 20 && 是8Bype的整数倍。
(2)没有虚函数的类对象继承关系在内存的存储方式
class Gradute : public Student{
private:
int _salary;
public:
char* _addr;
public:
Gradute(int, char*, int, int, int, char*);
~Gradute();
void Display();
void ClassType();
void FunctionGradute();
};
Gradute::Gradute(int sid, char* sname, int sage, int score, int salary, char* addr)
:Student(sid, sname, sage,score), _salary(salary){
int len = strlen(addr) + 1;
this->_addr = new char[len];
strcpy(this->_addr, addr);
}
Gradute::~Gradute(){
delete[] this->_addr;
}
void Gradute::Display(){
cout << "SID = "<<this->_sid<<endl;
cout << "SName = "<<this->_sname<<endl;
cout << "SAge = "<<this->_sage<<endl;
//cout << "Score = "<<this->_score<<endl;
cout << "Salary = " << this->_salary<<endl;
cout << "Address = " << this->_addr<<endl;
}
int main(void){
Student stu(100, (char*)"river", 22, 99);
Gradute grt(101, (char*)"g_river", 22, 100, 100000, (char*)"shanghai");
<span style="color:#ff0000;"> Student* pstu = &stu;</span>
Gradute* pgrt = &grt;
//stu.Display();
pstu->Display();
pgrt->Display();
//cout << cout.setf(ios::hex) << pstu << endl;
//cout << cout.setf(ios::hex) << &stu <<endl;
//cout << "sizeof(int) = " << sizeof(int)<<" " << "sizeof(char*) = "<< sizeof(char*) << endl;
//cout << "sizeof(stu._sname) = "<< sizeof(stu._sname) << endl;
//cout << "sizeof(Student) = " << sizeof(Student) << endl;
pstu = &grt;
pstu->Display();
cout << "sizeof(Student*) = " << sizeof(pstu) << endl;
cout << "sizeof(Student) = " << sizeof(stu) << endl;
cout << "sizeof(Gradute) = " << sizeof(grt) << endl;
return 0;
}
分析:存在继承关系时, 子类的内存存储方式是先存储父类的数据成员变量,在存储子类本身的数据成员变量。当父类指针指向
子类对象时,父类指针只是指向子类中父类的那片内存空间。细心的你会发现,当pstu=&stu, pstu->Display();是调用父类(Student)
中的Display()函数,当pstu=&grt, pstu->Display();仍然是调用父类(Student)中的Display()函数,但是,pgrt = &grt, pgrt->Display()
是调用子类(Graduate)中的Display()函数,这是为什么呢?
补充: this指针,想必大家都熟悉吧,可你曾想过this指针是什么东东?this指针是个特殊的指针,它指向本类对象,它的值是当前被
调用的成员函数所在的对象的起始地址,作为隐藏的参数传递给函数(包括构造函数和析构函数)。
例如: void Student::Display()经过编译器处理,void Student::Display(Student* const this)
void Graduate::Display()经过编译器处理,void Graduate::Display(Graduate* const this)
所以你才可以在编码的时候在函数内使用this指针调用数据成员,同时正是this指针的存在,才使类的函数代码可以重用并且数据
引用无误。因为每个类对象在调用函数时都会把自己的地址作为参数传入。所以, 当pstu=&grt时,pstu->Display()仍然是调用父类
中的Display()函数,编译器依据Display(Student* pstu)只能调用父类的Display()函数。(虚函数有些特殊,在后面为您详细讲解)
(3)没有虚函数的类对象多重继承关系在内存的存储方式
class Teacher{
public:
int _tid;
char* _tname;
protected:
char* _title;
public:
Teacher(int, char*, char*);
~Teacher();
void Display();
void ClassType();
void FunctionTeacher();
};
Teacher::Teacher(int tid, char* tname, char* title)
:_tid(tid){
int tnameLen = strlen(tname)+1;
this->_tname = new char[tnameLen];
strcpy(this->_tname, tname);
int titleLen = strlen(title)+1;
this->_title = new char[titleLen];
strcpy(this->_title, title);
}
Teacher::~Teacher(){
delete[]this->_tname;
delete[]this->_title;
}
void Teacher::Display(){
cout << "TID = "<< this->_tid << endl;
cout << "TName = " << this->_tname << endl;
cout << "Title = " << this->_title << endl;
}
void Teacher::ClassType(){
}
class Gradute : public Student, public Teacher{
private:
int _salary;
public:
char* _addr;
public:
Gradute(int, char*, int, int, int, char*, char*, int, char*);
~Gradute();
void Display();
void ClassType();
void FunctionGradute();
};
Gradute::Gradute(int sid, char* sname, int sage, int score,
int tid, char* tname, char* title,
int salary, char* addr)
:Student(sid, sname, sage,score),
Teacher(tid, tname,title),
_salary(salary){
int len = strlen(addr) + 1;
this->_addr = new char[len];
strcpy(this->_addr, addr);
}
Gradute::~Gradute(){
delete[] this->_addr;
}
void Gradute::Display(){
cout << "SID = "<<this->_sid<<endl;
cout << "SName = "<<this->_sname<<endl;
cout << "SAge = "<<this->_sage<<endl;
//cout << "Score = "<<this->_score<<endl;
cout << "Salary = " << this->_salary<<endl;
cout << "Address = " << this->_addr<<endl;
}
int main(void){
Student stu(100, (char*)"s_river", 22, 99);
Teacher ter(1000, (char*)"t_river", (char*)"Professor");
Gradute grt(100, (char*)"g_s_river", 22, 99,1000, (char*)"g_t_river",(char*)"Doctor", 100000, (char*)"shanghai");
Student* pstu = &stu;
Gradute* pgrt = &grt;
Teacher* pter = &ter;
//stu.Display();
pstu->Display();
pgrt->Display();
pter->Display();
//cout << cout.setf(ios::hex) << pstu << endl;
//cout << cout.setf(ios::hex) << &stu <<endl;
//cout << "sizeof(int) = " << sizeof(int)<<" " << "sizeof(char*) = "<< sizeof(char*) << endl;
//cout << "sizeof(stu._sname) = "<< sizeof(stu._sname) << endl;
//cout << "sizeof(Student) = " << sizeof(Student) << endl;
pstu = &grt;
pstu->Display();
pter = &grt;
pter->Display();
cout << "sizeof(Student*) = " << sizeof(pstu) << endl;
cout << "sizeof(Student) = " << sizeof(stu) << endl;
cout << "sizeof(Teacher) = " << sizeof(ter) << endl;
cout << "sizeof(Gradute) = " << sizeof(grt) << endl;
return 0;
}
分析: 多重继承时, 子类对象内存存储方式是按照继承父类的声明顺序进行存储。
例如class Graduate : public Student, public Teacher; 那么子类内存存储方式是先存储父类Student的数据成员,再存储父类
Teacher的数据成员,最后存储子类自身的数据成员。
如果class Graduate : public Teacher, public Student; 那么子类内存存储方式是先存储父类Teacher的数据成员,再存储父类
Student的数据成员,最后存储子类自身的数据成员。
这个父类的继承顺序同样也是子类(Graduate)构造函数调用父类的构造函数调用的顺序。