理论知识:C++类及OOP编程
开门见山:OOP(面向对象编程)的特点:
- 抽象
- 封装和数据隐藏
- 多态
- 继承
- 代码的可重用性
关于类:
C++中关于类的应用,是面对开发者的,为了保持类的稳定系,需要对【数据成员】进行数据隐藏,也就是放在private中。
原因非常简单,如果开发者都可以修改了,那么类的稳定性就会差很多。
但是为了开发者,还是有一些内容是public的,是开发者可以直接调用的
那么这个部分就叫【类方法】,也叫【公共接口的类成员】。这就叫抽象,将众多复杂问题的核心抽象出来变成函数。
以上概念众多,那么如何更好的理解呢?
乙方将【公共接口的类成员】设置好,这些接口是要面对甲方的,甲方根据需要选择,这部分的成员是public的,因为要面向甲方,甲方必须有权限进行自由的调用及使用。
那么乙方为了保护用品的稳定性,不可能将所有内容都公开,那么【公共接口的类成员】及【类方法】使用过程中调用的变量,均为private,这部分内容甲方不可见也不可调用。
以上乙方做的工作,统称为封装。
封装:将实现细节放在一起并将它们与抽象分开被称为封装
数据隐藏是一种封装,将类函数定义和类声明放在不同的文件中也是一种封装。
具体关于类的内容:
类的定义分为以下两个部分
类声明:
- 类声明:以数据成员的方式描述数据部分,以成员函数(类方法)的方式描述公有接口
- 类方法定义:描述如何实现类成员函数
一般将接口(类方法)放在头文件中(类声明),并将实现(类方法的代码)放在源代码文件中(类方法定义)。
stock00.h//类声明
class Stock
{
private:
std::string company;
long shares;
double share_cal;
public:
void acquire(const std::string & co,long n,double pr);
void buy(long num,double price);
void sell(long num,double price);
}
stock00.cpp//类方法定义
#include"stock00.h"
void Stock::acquire(const std::string & co,long n,double pr)
{
company = 0;//类方法是可以访问私有成员的
.......
}
void Stock::buy(long num,double price)
{
shares = 0;//类方法是可以访问私有成员的
.......
}
void Stock::sell(long num,double price)
{
share_cal = 0;//类方法是可以访问私有成员的
.......
}
应用者如何面对:
使用成员运算符
Stock kate,joe;
kate.show();
joe.show();
用就行了,而且创建的每个新对象都有自己的存储空间,用于存储其内部变量和类成员;
但是同一个类的所有对象共享同一组类方法,及每种方法只能有一个副本。
kate.show()占领一部分内存,而joe.show()占领另一部分内存。
但是执行show()代码使用的是同一段代码。
要使用某个类,必须了解其公共接口,要编写类,必须创建其公共接口。
相关题目:
类的大小:类的大小是个问题,类的有静态关键字修饰的成员变量和函数,虚函数指针,有时候甚至涉及虚继承
总结:
- 普通的成员函数,构造和析构函数,都不会占用空间,因为非内联成员函数(non-inline member function)只会诞生一份函数实例。
- 静态关键字修饰的成员变量不会在类中占用空间
虚函数有虚函数指针,占用4个字节(和虚函数个数无关,所有的偏移量都存在虚函数表里面)
虚继承有虚基类指针,占有4个字节
下面我们来看看几具体的实例:
1和2两点一起进行测试:
#pragma pack(1)
class student
{
public:
student();//构造函数(不占空间)
int a = 0;
static int b;//静态成员变量(不占空间)
void show();//成员函数(不占空间)
~student();//析构函数(不占空间)
};
class test1:public student
{
public:
int b;
};
qDebug()<<"sizeof(student)"<<sizeof(student)<<endl;
qDebug()<<"sizeof(test1)"<<sizeof(test1)<<endl;
输出:
这都很好理解,下面我们看虚函数的情况
class student
{
public:
student();//构造函数(不占空间)
int a = 0;
static int b;//静态成员变量(不占空间)
void show();//成员函数(不占空间)
~student();//析构函数(不占空间)
virtual void testvirtual1();
virtual void testvirtual2();
virtual void testvirtual3();
};
class test1:public student
{
public:
int b;
void testvirtual1();
void testvirtual2();
void testvirtual3();
};
和上一次相比,多了4个字节,就是多了虚函数指针的大小。
多继承:
成员变量和单继承情况一样:
#pragma pack(1)
class student
{
public:
int a = 0;
};
class test1
{
public:
int b;
};
class test2
{
public:
int c;
};
class test3:public test1,public test2,public student
{
public:
};
那么虚函数呢?也是一样的
#pragma pack(1)
class student
{
public:
// int a = 0;
virtual void testvirtual1();
virtual void testvirtual2();
virtual void testvirtual3();
};
class test1
{
public:
// int b;
virtual void testvirtual1();
virtual void testvirtual2();
virtual void testvirtual3();
};
class test2
{
public:
// int c;
virtual void testvirtual1();
virtual void testvirtual2();
virtual void testvirtual3();
};
class test3:public test1,public test2,public student
{
public:
// int d;
void testvirtual1();
void testvirtual2();
void testvirtual3();
};
我们看看多重继承下,虚函数表的情况
虚函数不覆盖的情况如上图所示,覆盖的情况如下图所示
会顺位覆盖第一个类中的内容。
所以回头再看程序,我们就会知道
student :自己的虚函数指针:4
test1:自己的虚函数指针:4
test2:自己的虚函数指针:4
test3:虚函数指针x3:12
最后是最复杂的情况:虚继承,因为编译器的不同,对虚继承层次的对象的内存布局,在不同编译器实现有所区别。
1:空类的大小是多少?答案:1
空类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。
C++标准指出,不允许一个对象(当然包括类对象)的大小为0
2:
在32位系统下,sizeof(a)的值是()
int 4个字节
union U 13+4个字节, union占对齐后数据的最大字节大小,默认为13+3=4*4=16;
因为是共用体,其大小是共用体的最大成员长度
此时按照13算,4对其自然是16字节
但是,该处强制为2字节对齐,实际为13+1=2*7=14字节
color 枚举类型的实例 4字节
但是程序明确。#pragma pack(2)
所以:4+14+4 = 22