C++面向对象的编程基础
什么是面向对象的编程
面向对象的编程就是面向具有同一种属性的事物
这里有两种提取方法:一是从上到下的分类。提取的过程就是将事物的共有属性拿出来放到一起,进而形成一个特有的抽象指代名词,而暂时忽略每个事物的个性。而当个性被派上用场是,再从底层相同性中派生出个性。这就是派生类的由来。
面向对象的基本内容
程序运行期的基本实体——对象
对象就是在类之下的 它拥有抽象名词的共有属性加上自己的个性
实现面向对象的变成的必要条件——类
类就是具体对象所属的一个派别名称
类拥有自己的成员变量和成员函数,它是一些事物相同特性的体现者
类声明一般形式 class 类名
class Dog 定义一个名为Dog的类
{
};
Dog doudou; 声明Dog类的一个对象
实现程序的模块化——封装
封装就是将大程序分成几个实现不同功能的小模块,实际应用中又有对类的封装和对函数的封装。
封装就像是黑盒子,黑盒子就叫函数体,通风口叫参数接口,这是对于函数的封装
下面介绍以下对类的封装
因为类中既有变量成员又有函数,所以它的接口比函数要多
实现系统的可重用及可扩充性——继承
继承能够使原有属性被反复利用,从而能够减少代码的书写量使代码更有逻辑性
同意消息为不同对象接受时产生不同的行为——多态
多态的运行涉及到了virtual和重写这两个概念,多个对象都是由同一个基类派生来的,这就是多态
对象之间沟通的途径——消息传递
讨论类的结构和编写规则
类的创建:最基本的方法就是看是否有构造和析构这样的成员函数
C++中类的成员变量分为三类:公有、私有、保护
公有成员 public 在其生命周期内,在任何地方都可以被访问
私有成员 private 只能被该类的其他成员访问,外部函数甚至他的子类就不能进行访问
保护成员 protected 与私有成员一样,但是它可以被其子类访问
构造函数
创建规则
构造函数的名字必须和本类的类名一样
构造函数不可以有任何类型的返回值,即使是void也不行
一个类的构造函数可以被重载,即可以由多个构造函数
构造函数定义体 类名::类名()
实现功能:给被建立的对象进行初始化,所以如果一个类没有构造函数,系统就会自动给类生成默认的构造函数,但是该构造函数只会执行创建对象的操作,不会给对象执行任何初始化工作。
何时被调用
#include"stdafx.h" #include <iostream> using namespace std; class Baby { public: Baby(); ~Baby(); }; class Family { public: Family(); ~Family(); private: Baby bb; }; Family::Family() { cout << "Family构造函数被调用" << endl; } Family::~Family() { } Baby::Baby() { cout << "Baby构造函数被调用" << endl; } Baby::~Baby() { } int main() { Family family; return 0; }
默认参数的构造函数
默认构造函数和默认参数的构造函数不是一回事
#include "stdafx.h" #include <iostream> using namespace std; class Adult { public: Adult(int x = 1,int y = 1); ~Adult(); private: int X; int Y; }; Adult::Adult(int x, int y) { X = x; Y = y; cout << "(" << X << "," << Y << ")"<<endl; } Adult::~Adult() { } int main() { Adult adult1; Adult adult2(20); Adult adult3(10, 20); return 0; }
通过上面的代码可以看出,如果对象没有初始值就使用默认值;如果只给一个初始值则第二个参数值不变,第一个参数用被提供的值,如果给两个初始值,就用初始值。
默认构造函数和默认参数的构造函数不能同时存在,因为如果两个同时存在的话,一旦程序编译,语句不知道该调用哪个构造函数
析构函数
当对象要被销毁的时就会调用类的析构函数
创建规则:
同构造函数一样无返回值,也无参数。
函数是在构造函数名之前加一个"~"运算符
不可以被重载,读者可以在析构函数体中自行写释放资源的语句也可不写
执行功能:当一个类的对象被构造函数初始化,计算机会分配给它一部分资源。就像纸张,类对象也是如此,当对象的生命终点的时候就需要被销毁,它在被销毁之前就需要释放在运行过程中所占的资源。从而回到初始状态,这就是析构函数。
何时调用
如果类中没有析构函数,系统会自动为对象加载一个默认的析构函数
就是调用顺序
1.无其他类对象
析构函数总是在构造函数之后被调用
2.有其他类对象
初始化对象的时候,先调用被调用类的构造函数,再调用主类的构造函数,而释放资源时先释放主类的,再释放被调用类的。
实例:介绍重载构造函数和复制构造函数
重载构造函数
构造函数不同于析构函数
1 class Baby 2 { 3 public: 4 Baby() 5 Baby(int age); 重载构造函数 6 ~Baby(); 析构函数 7 private: 8 int baby_age; 成员变量 9 }; 10 Baby bb1; 调用默认构造函数 11 Baby bb2(1); 调用带参构造函数
复制构造函数
结构体中可以直接将name1的值赋给name2
在C++中对于相同类定义的不同对象,可以用一个对象去构造另一个对象。此时被赋值的另一个对象调用的构造函数就被称为复制构造函数。
#include "stdafx.h" #include <iostream> using namespace std; class Adult { public: Adult(){} Adult(int z) {Z = z; } void display() { cout << Z << endl; } ~Adult(){} private: int Z; }; int main() { Adult adult1(1); Adult adult2; adult2 = adult1; adult1.display(); adult2.display(); return 0; }
虚函数
功能作用:虚函数就是为实现多态而准备的一种类型函数,还要在其子类中对这个虚函数重新定义,就是基类中的函数被“虚”之后,就可以在子类中进行重写啦。另外从基类派生的子类可以实现多态,但从子类中又派生出的子类也可以形成多态。
具体书写功能
1 #include "stdafx.h" 2 #include<iostream> 3 using namespace std; 4 5 6 class Human 7 { 8 public: 9 virtual void Move() { cout << "人类的移动方式" << endl; } 10 }; 11 12 class Person_jia : public Human 13 { 14 public: 15 /**virtual*/ void Move() { cout << "路人甲采用步行移动"<<endl; } 16 17 }; 18 19 class Person_yi : public Human 20 { 21 public: 22 /**virtual*/ void Move() { cout << "路人乙采用乘公交移动"<<endl; } 23 24 }; 25 26 class Num1_bus : public Person_yi 27 { 28 public: 29 /**virtual*/ void Move() { cout << "路人乙采用乘916路公交移动"<<endl; } 30 31 }; 32 33 class Num2_bus : public Person_yi 34 { 35 public: 36 /**virtual*/ void Move() { cout << "路人乙采用乘12路公交移动"<<endl; } 37 38 }; 39 40 class Person_bing : public Human 41 { 42 public: 43 /**virtual*/ void Move() { cout << "路人丙采用开QQ车移动"<<endl; } 44 45 }; 46 47 int main() 48 { 49 Person_jia fangshi1; 50 Person_yi fangshi2; 51 Num1_bus fangshi3; 52 Num2_bus fangshi4; 53 Person_bing fangshi5; 54 55 Human *human[5] = { &fangshi1,&fangshi2,&fangshi3,&fangshi4,&fangshi5 }; 56 57 for (int i = 0; i < 5; i++) 58 { 59 human[i]->Move(); 60 } 61 62 63 return 0; 64 }
详解派生类
派生类就是从基类中继承而来的类,所有的类都可以派生出一个新类,原先的类被叫做基类或父类。新类被叫做派生类或者子类。
派生类从何派生而来
派生类是单一继承而来
派生类是多重继承而来的 那它就可以拥有多个基类的性质
如何书写 多重继承:class 派生类名 : 继承方式 基类名1,继承方式 基类名2,...,继承方式,基类名n
#include "stdafx.h" #include<iostream> using namespace std; class Image { public: Image() { cout << "Image construct" << endl; } ~Image() { cout << "Image deconstuct" << endl; } }; class Bmp : public Image { public: Bmp() { cout << "Bmp construct" << endl; } ~Bmp() { cout << "Bmp deconstruct" << endl; } }; int main() { Bmp bmp; return 0; }
多重继承就是在继承方式基类名后 加上多个继承方式和基类名就可以了
友元
C++友元的出现是因为封装,外部变量不可以访问私有成员。就是封装的整体对友元开放
C++描述友元的关键字是friend友元可以是一个函数也可以是一个类
class Arithmetic { public: Arithmetic(int_a = 0, int_b = 0); void show(); friend Arithmetic add(Arithmetic &arith); friend int sub (Arithmetic &arith); private: int a, b; };
class Ada_Arithmetic { public: Ada_Arithmetic(); ~Ada_Arithmetic(); friend class Arithmetic; private: int x,y; };
重载
重载就是对现有事物进行另一种方式的描述
函数的重载
函数的名字必须一模一样
在函数参数个数相同条件下,参数的类型必须不同
在函数的参数类型相同的条件下,参数的个数必须不同
以上三种条件满足情况下,函数的返回类型值可相同也可不同
重载和重写的不同
重写:因为应用于多态的实现,所以只能在被派生类中。另外被重写的函数必须跟基类的函数一模一样,不管是“返回值类型”、“函数名”、“参数列表”
功能作用
重写:针对不同对象实现相同功能
重载:针对不同对象实现不同功能
#include "stdafx.h" #include<iostream> using namespace std; double Distance(int array1[2], int array2[2]) { double distance = 0; distance = sqrt((float)((array1[0] - array2[0]) * (array1[0] - array2[1])) + (array1[1] - array2[1]) * (array1[1] - array2[1])); return distance; } double Distance(float array1[2], float array2[2]) { double distance = 0; distance = sqrt((float)((array1[0] - array2[0]) * (array1[0] - array2[1])) + (array1[1] - array2[1]) * (array1[1] - array2[1])); return distance; } int main() { int a[2] = { 1,1 }; int b[2] = { 3,3 }; cout << "整型点{1,1}和{3,3}间的距离为:" << Distance(a, b) << endl; float c[2] = { 1.0,1.0 }; float d[2] = { 0.0,1.0 }; cout << "浮点型点{1.0,1.0}和{0.0,1.0}间距离为:" << Distance(c, d) << endl; return 0; }
运算符的重载就是对运算符的重新定义即可
实例:具体实现封装、继承

浙公网安备 33010602011771号