C++面向对象

面向对象编程(oop:object oriented programming)

类(class)–>实例化—>对象(object):内存中具有类型的区域。
对象拥有:属性(atribute)<静态>和方法(method)<动态>
成员:属性方法对象都称为成员
属性也称为数据成员(data member)或成员变量(member variables)
方法也称为成员函数(member function) 或函数成员

类所占存储空间大小至少为其所有非静态数据成员所占空间的总和,还要考虑内存对齐机制的影响,和结构体大小的分析完全一样。一个类的所有对象共享一份方法方法代码。

访问权限修饰符:说明类中成员的访问权限

Private:私有,只能在类体中访问,(或者通过方法间接调用)。
Protect:保护,为子类而生。
Public:公有,都可以访问。

C++开发规范:
类的声明和实现分离,将类的声明放在头文件中,实现放在源文件中,文件名的主文件名都为类名。

This指针:类中的隐含数据成员,代表当前对象的指针,不占存储空间。

构造函数和析构函数:

构造函数(constructor,构造器):对新创建对象的数据成员进行初始化。在定义一个新对象时,根据定义对象的语法形式,有且只有一个匹配的构造函数被自动调用。
构造函数的特点:
1没有返回值。
2.函数名必和类名相同。
3.不能被声明为const的。
4.可以被重载。但是有且只有一个匹配的构造函数会被自动调用。

如果我们在定义类时,不定义任何构造函数,C++编译器会这个类自动添加一个默认的无参构造函数(函数体为空),即这个默认的构造函数什么都不做。
如果我们在定义类时,不定义拷贝构造函数,那么C++编译器会给这个类自动添加一个默认的拷贝构造函数,它实现的就是数据成员的重新赋值,即浅拷贝。要想实现深拷贝,需亲自定义。

带一个参数的构造函数也称为转换构造函数,它可以实现将其他类型数据转换为类的对象。

析构函数(destructor,析构器):主要执行最后的清理工作,它是一个特殊的成员函数
在一个对象被销毁之前,系统会自动调用其析构函数,等析构函数执行结束返回后系统才会释放该对象所占的存储空间,即该对象被彻底销毁。
析构函数的特点:
1.不允许重载
2.函数名为~和类名构成
3.没有返回值
4.可以被显示调用

连续定义多个对象时,先定义的先构造,后定义的后构造。析构时,先定义的后死,后定义的先死。(栈)

New delete和malloc free 的重要区别
new = malloc+构造;delete = 析构+free
(对于类多用new和delete,对于基本数据类型没什么区别)
1.malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符。
2.new能够自动分配空间大小。

类和类之间的关系

1.没关系
2.友员类
3.组合关系(包含关系,has a关系)
4.继承关系(父子关系,从属关系,is a 关系)

友员类:

我们已知道类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。
为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数或类,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。**友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。**不过,类的访问权限确实在某些应用场合显得有些呆板,从而容忍了友元这一特别语法现象。
友元函数是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样。
**友元关系不具对称性。**即 A 是 B 的友元,但 B 不一定是 A 的友元。友元关系不具传递性。即 B 是 A 的友元,C 是 B 的友元,但是 C 不一定是 A 的友元。
作用及特点
友元提供了不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。通过友元,一个不同函数或另一个类中的成员函数可以访问类中的私有成员和保护成员。c++中的友元为封装隐藏这堵不透明的墙开了一个小孔,外界可以通过这个小孔窥视内部的秘密。
友元的正确使用能提高程序的运行效率,但同时也破坏了类的封装性和数据的隐藏性,导致程序可维护性变差。
下面举一例子说明友元函数的应用。

#include<iostream>
#include<cmath>
using namespace std;
class Point
{
	public:
	Point(double xx, double yy){
		x = xx;
		y = yy;
	};
	void Getxy();
	friend double Distance(Point &a, Point &b);//友元函数,引用类中的私有成员
	private:
		double x, y;
};
void Point::Getxy()
{
	cout << "(" << x << "," << y << ")" << endl;
}
double Distance(Point &a, Point &b)
{
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return sqrt(dx*dx + dy*dy);
}
//该程序的功能是已知两点坐标,求出两点的距离
int main(void)
{
	Point p1(3.0, 4.0), p2(6.0, 8.0);
	p1.Getxy();//p1.Getxy()和p2.Getxy()这是成员函数的调用,要用对象来表示。
	p2.Getxy();
	double d = Distance(p1, p2);//同普通函数的调用一样,不要像成员函数那样调用
	cout << "Distance is" << d << endl;
	return 0;
}

友元除了前面讲过的函数以外,友元还可以是类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。
定义友元类的语句格式如下:
friend class 类名(即友元类的类名);
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明。
友元函数不容易解决的问题

即一个函数作为一个类的函数,同时又是另一个类的友元。
如果我们决定该函数必须作为一个类的成员函数,同时又是另一个类的友元,则成员函数声明和友元声明如下:

class Window;
class Screen
{
	public:
	//copy是类Screen的成员
	Screen& copy(Window&);
	//...
};
class Window
{
	//copy是类Window的一个友元
	friend Screen& Screen::copy(Window&);
	//...
};

只有当一个类的定义已经被定义时,它的成员函数才能被声明为另一个类的友元。例如Screen 类必须把Window 类的成员函数声明为友元,而Window类必须把Screen 类的成员函数声明为友元。在这种情况下可以把整个Window类声明为Screen 类的友元。

Screen 类的非公有成员现在可以被Window 的每个成员函数访问。

继承:

类的组合:即类的嵌套,即类中的属性为另外某个类的对象,这种属性我们也称为子对象(child object)
子对象的初始化问题
嵌套时,构造:先轮胎后车;析构:先车后轮子

继承(inberitance):在一个已经存在的类的基础上创建一个新类
父类(father class)或基类(base class)
子类(child class)或者派生类(derived class)
子类继承父类,父类派生出子类
子类会无条件获得其父类中所有成员(不包括构造函数和析构函数),另外子类也可以增加新成员

3种继承方式:
父类中私有成员在子类不可访问(不可见)
public:共有继承,父类中的公有的保护成员在子类中还是保持不变,
protected:保护继承,父类中的公有成员在子类中都变为保护的,
private:私有继承,父类中的公有和保护成员在子类中都变为私有的
公有继承最常用,方便多级继承;

在定义子类对象时,父类构造函数先调用,用以初始化从父类继承过来的数据成员,子类构造函数后调用,用初始化子类新增的数据成员
在子类对象的释放时,子类析构函数先调用,父类析构函数后调用
父先生,子再生;子先死,父再死。

同名覆盖:属性同名,方法和同名并且形参类型列表相同

子类和父类类型转换问题;
1.子类对象可以转型为父类对象,此时子类新增的属性将被截断,因为它们存放在高字节处,其实得到的就是从父类继承的部分。父类对象转型为子类没有意义,因为子类对象所占存储空间往往比父类对象大。
2.父类引用或指针指向子类对象是可以的,此时它们指向的其实是子类对象从父类继承的部分,但反之没有意义

一级继承
多级继承:直接子类,间接子类;直接父类,间接父类

单继承(single inheritance):子类的直接父类只有一个。
多重继承(multiple inheritance):子类的直接父类有多个。
多重继承的例子:骡子继承了马和驴的特点
会形成菱形继承,虚继承可以避免 virtual

虚继承和虚基类

多态(polymorphism):多种状态,在面向对象编程中,调用不同对象的同种方法会产生不同的行为。多态是通过虚函数实现的。
虚函数:用virtual关键字修饰的方法。
如果父类中某个方法为虚函数,那么子类中的该方法也自动为虚函数,可以不用加virtual关键字,但为了增加可读性,建议加上virtual关键字修饰。
如果子类重新实现了父类中的某个虚函数,我们称为子类了重写了(overwrite,override)该方法。
如果子类重新实现了父类中的某个非虚函数,我们称为同名覆盖。(虚拟函数的同名覆盖就是重写)
虚函数只是继承了原函数的指针,没有新的形成,非虚则是拷贝了一份。

多态机制:将子类对象的指针赋给父类指针变量,或将子类对象赋给父类引用,通过父类指针或引用去调用父类中的某个虚函数,如果子类重写了该虚函数,那么调用的将是子类中的虚函数,否则将调用父类中的虚函数。

纯虚函数(Pure Virtual Function)和抽象类(Abstract Class)

纯虚函数:只有声明没有实现的虚函数,并且要在声明部分的后面添加“= 0”
抽象类:含有纯虚函数的类
抽象类不能被实例化,即通过抽象类不能定义任意对象,抽象类通常是用来作为其他类的父类使用的,即抽象基类。
如果一个抽象类的子类不重写其父类中的所有纯虚函数,那么该子类也是抽象类,不能用来定义对象,反之它将是一个具体类(concrete class),可以用来定义对象。

虚析构函数(virtual destructor)
在堆区中定义一个子类对象,然后将其指针赋给父类指针变量,最后通过父类指针去释放该子类对象时,
如果父类的析构函数不是析构函数,那么只会调用父类的析构函数,而子类的析构函数不会被调用,可能会导致内存泄露,
如果父类的析构函数为虚函数,那么首先会调用子类的析构函数,然后再调用父类的析构函数。
C++编码规范:在定义一个类时,将他的析构函数定义为虚函数

虚函数表(V-table,vftable):C++多态机制是通过虚函数实现的
如果一个类中含有虚函数,那么该类会对应虚函数表,用于存放各个虚函数的指针,该类的所有对象所占存储空间的开始四个字节都是指向这个表头的指针
表中的存放各个虚函数方法的指针

子类不重写相当于只是拷贝了一份
重写了方法对应的虚函数表的指针会变(指向重定义的方法)

虚继承是通过虚基表(vbtable)实现的

类中的成员包含属性,方法,类型,枚举常量等

C++中结构体和类几乎一样,也支持方法,继承和多态,C语言中传统的结构体(只包含数据成员),面向对象编程采用类,而不是结构体。
C++中结构体和类的区别:
1.成员默认的访问权限不同,结构体为public,类为private
2.默认继承方法不同,结构体为public,类为private

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的提千万

江山父老能容我,不使人间造孽钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值