面向对象的程序设计
概念:(Object Oriented Programming,缩写:OOP)是一种程序设计范型,同时也是有一种程序开发的发方法。
对象指的是类的实例,将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。
面向对象的三大特性
封装、继承、多态
1)封装
封装的定义:是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数剧的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
封装的目的:增强安全性和简化编程,使用者不必了解具体的实现细节,而只是通过外部接口,一特定的访问权限来使用类的成员。
例如:在抽象的基础上,我们将日期的数据和功能封装起来,构成一个日期类。
例子:
#include <iostream>
using namespace std;
class Date//Date为类名
{
public:// public为访问限定符,共有成员,外部接口
void Display()//成员函数
{
}
private:
int* _year;//成员变量
int* _month;
int* _day;
};
2)继承
继承:是面向对象程序设计中最重要的一个概念,继承允许我们依据另一个或多个类来定义一个新类,这使得创建和维护一个应用程序变得更容易。这样做也达到重用代码功能和提高执行时间的效果。依据已有类称为基类,新建的类称为派生类。如果一个类A继承了另一个类B,那么A称为B的子类,而把B称为A的父亲。
继承:可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码,在让子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类原有的属性和方法,使其获得与父类不同的功能。
例子:
class shape //物体的形状
{
public:
void setwidth(int w)
{
_width = w;
}
void setheight(int h)
{
_height = h;
}
protected:
int _width;
int _height;
};
//派生类
class Rectangle :public shape //长方形物体
{
public:
int getArea()
{
return (_width*_height);
}
};
3)多态
多态:按字面的意思就是多种形态。根据所属,又把多态分为两种:类间、类内
①类内多态:就是函数的多态。函数名相同,函数的参数个数、参数的类型是不同的,来拓展一个函数的功能。(又叫重载,这是一种编译时的多态)
②类间多态:是通过虚函数来进行实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法叫做覆盖(override),或者称为重写(这是一种运行时多态)
多态的作用:
为了接口的复用,也就是说,不论传递过来的究竟是哪个类的对象,函数都能通过同一个接口调用到适应各自对象的实现方法。
例子:
#include <iostream>
using namespace std;
class A
{
public:
void foo()
{
cout << 1 << endl;
}
virtual void fun()//是C++中定义虚函数的关键字
{
cout << 2 << endl;
}
};
class B:public A
{
public:
void foo()
{
cout << 3 << endl;
}
void fun()
{
cout << 4 << endl;
}
};
int main(void)
{
A a;
B b;
A* p = &a;
p->foo();
p->fun();
p = &b;
p->foo();
p->fun();
system("pause");
return 0;
}
//输出的结果为1 2 1 4,foo()函数为不是一种多态,基类中的foo()函数不是虚函数
怎样去看待类
C++在C语言的基础上增加了面向对象编程,C++支持面向对象程序设计,类是C++中核心的特性,通常被称为用户定义的类型,类是对问题的抽象化描述,是对数据和程序的进行的封装,类是把属性和方法进行封装,同时对类的属性和方法进行访问控制。
三种访问限定符
eg:
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
cout << 1 << endl;
}
virtual void fun()
{
cout << 2 << endl;
}
void test()
{
cout << 5 << endl;
}
private:
void BasePrivate()
{
cout << 6 << endl;
}
};
class B : public A
{
public:
void foo()
{
cout << 3 << endl;
}
void fun()
{
cout << 4 << endl;
}
void ChildTestBasePrivate()//出错
{
BasePrivate();
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo();
p->fun();
p = &b;
p->foo();
p->fun();
p->test();
system("pause");
return 0;
}
- pubic成员可以从类外部直接进行访问,private/protected成员不能从类外部直接直接访问
- 每个限定符可以在类体中使用多次,它的作用域是从该限定符出现到下一个限定符之前或类体结束前。
- 类体如果没有定义限定符,就默认为私有的。(struct默认是公有的)
- 类的访问限定符体现了面向对象的封装性。
类的作用域
- 局部域
- 全局域
- 类域
名字空间域
类的作用域
1)每个类都定义了自己的作用域,类的成员(成员函数/成员变量)都在这个作用域中,成员函数内可以任意访问成员变量和其他成员函数。
2)对象可以通过直接访问公有成员指向对象的指针通过->也可以直接访问到公有成员。
3)在类外定义成员,需要使用::作用域解析符指明成员属于哪个类。
类实例化对象
1)类知识一种模型,定义出来了,并没有分配实际的内存空间来存储。
2)一个类可以实例化许多对象,实例化出的对象占用实际的物理空间存储类成员变量
3)类向盖房子的图纸,对象就是建造好的房子,类只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
类对象存储模型
每个对象的大小为类中所有成员变量的大小之和,当然也遵循内存对齐的原则。
当对象被创建的时编译器就为每个对象分配内存空间,包括成员变量和成员函数。
只关的认识就是,假设我们定义了10个对象,就要为这10个对象的变量和成员函数分配内存空间。如下图所示:
虽然每个对象的成员变量是不一样的,但是成员函数的代码是相同的,上面的内存模型保存了10份相同的代码片段,浪费了很多的空间。
事实上,编译器会将成员变量和成员函数分开存储,分别为每个对象的成员变量分配内存,但是所有对象的成员函数都共享同一段函数代码。如下图:
一、为什么要有内存对齐?
进行内存对齐的作用主要有两个:
1)平台移植:不是所有的硬件平台都能够访问任意地址上的数据
2)性能:内存对齐后访问速度提升了(对于访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问仅需要一次访问)
为什么内存对齐会提升效率?
CPU把内存当成是一块一块的,块的大小可以是2、4、8、16字节等大小。CPU在读取内存的时候是一块一块读取的。块大小即memory access granularity:内存读取粒度。
假设CPU要读取一个int型4字节大小的数据,看下列2种情况:
(1)数据从0字节开始;
(2)数据从1字节开始;
假设内存读取粒度是4.
情况(1)的时候,CPU只需一次便可把4字节读取出来。
但是情况(2)的时候,要复杂一些。这个时候CPU先访问一次内存,读取0-3字节进寄存器,再读取4-7字节进寄存器,然后把0、6、7、8字节的数据删除掉,最后合并1-4字节的数据。可以看出,如果内存没有对齐,所进行的操作要复杂得多。
二、空类对象(无成员变量的类)的大小是多少?为什么?
空类的大小是1字节,主要是为了占位,表示自己存在过。
#include <iostream>
using namespace std;
class C
{
;
};
int main()
{
cout << sizeof(C) << endl;
system("pause");
return 0;
}
三、A和B的大小是多少?
#include <iostream>
using namespace std;
class A
{
char ch;
double d;
};
class B
{
char ch1;
A a;
char ch2;
};
int main()
{
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
system("pause");
return 0;
}
运行结果:
注意:在VS中默认的对齐数为8;在gcc中默认的对齐数是4;
对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
规则:
(1)数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
(2)结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
(3)当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。