封装总结
一、面向对象之前的内容总结
1)C++特点
C++是一门高级的 编译型,静态类型的编程语言,它支持面向过程编程,它支持面向对象编程,它还支持泛型编程。
2)C++的基本数据类型
C语言中学过的基础类型 + 布尔类型bool +引用类型 (数据类型&)
3)C++的高级数据类型
数组,结构体,枚举,联合体 + 类类型 +其他类型 (string ,list,map,vector等)
说明:C++支持两种字符串类型,一种是从C语言继承过来的类型(用字符数组来表示字符串),新增string
结构体做增强:允许结构体中存放函数成员
4)运算符
主要传统的运算符的基础上: ::作用域限定符, 引用运算符&
5)强制类型转换
新增了一种新的转换方式:static_cast<目标类型>(表达式)。 在C++中不推荐C语言那种强制转换方式。
6)C++的源码文件的扩展名 cpp 头文件也用.h
-
C++标准输入与输出
#include
using namespace std;
cout<<表达式或常量<<...
cin>>变量名;
8)三大程序结构 和C语言完全一样。
9)函数章节
内联函数
作用:为了提高程序的执行效率。
格式:inline 返回类型 函数名(形参列表)
{
}
场景:需要频繁本调用 且函数体不超过5条语句。
函数重载:在同一个作用域下,可以定义多个同名函数,且要确保这些函数的形参或类型不一样的函数集合
函数模版:为了减少代码冗余,提高程序的灵活性,让用户有更多的时间来专注业务逻辑。
格式:template<typename 虚拟符号1,.....>
返回类型 函数名(形参列表)
{
}
默认形参:在C++中对函数的形参类型新增了一种 叫默认形参 :数据类型 形参名=默认值
注意:1.默认形参要定义在普通形参的后边2.定义默认形参时 定义函数和函数原型只保留一个。3.默认形参对重载可能会影响
新增了一种函数的传参方式:引用传参
1)引用:就是给某个变量起一个新的别名。
数据类型 &别名=变量名;
2)引用类型可以修饰函数的形参,也可以修饰函数的返回类型
10) auto ---类型推断
auto a=1;
二、面向对象编程简介
1)编程思想:就是在程序世界对现实世界问题的建模的方法
主要的编程思想:面向过程编程思想,面向对象编程思想
2)面向过程编程
核心:主要强调"怎么做",它将要解决的问题拆分成多个步骤,然后用函数来描述每个步骤,最后按照业务流程来调用逐个函数
来实现问题的最终解决。它以函数作为基本单位来组织我们的代码。
3)面向对象编程
核心"谁来做", 它是遇到一个问题,先分析这个问题域中会涉及到那些角色,然后先用类来抽象出各个角色,然后在根据业务流程
来调用各个角色中成员来实现最终问题的解决,它是以类为单位来组织我们的代码。
4)适用场景:面向过程编程适用于小规模代码的场景,中大规模的问题适合用面向对象编程。
5)什么是类
类就是对一类事物的公有属性和行为抽象的集合
对象:类的具体实例
关系:类是模版,对象时通过类创建的实例,一个类可以创建多个对象,这些对象就自然而然拥有了该类所拥有的属性和行为。
6)面向对象的三大特性
封装:将数据和操作数据的行为组合在一起,形参一个整体,对外隐藏细节,需要向外界通信的 可以暴漏接口。
作用:增强数据的安全性,简化编程。
继承:通过继承,父类有的,子类可以直接拿来使用,不需要从零做起,父类没有的 子类可以对象进行新增或扩展。
它是用来描述类与类之间的关系 。
作用:为了实现代码的更好复用
多态:不同对象在调用相同接口的时候,所表现的不同行为。
作用:为了提供程序的灵活性。
三、面向对象编程之封装
结构体类型和类类型是C++给用户提供一种自定义数据类型的手段
1)如何定义一个类
class 类名
{
类体=访问范围修饰符+数据成员列表+函数成员列表
};
访问范围修饰符:主要设置成员对外的可见性
数据成员列表:主要用来描述事物的属性
函数成员列表:主要用来描述事物的行为
2)创建对象
栈上建对象: class 类名 对象(实参列表); //要不要加实参列表 取决于该类有没有默认构造
释放:自动释放,程序员不需要干预
构建多个对象:要么构建一个对象数组,要么对象之间用逗号隔开 stu s1,s2;
在堆上建对象: new 类名(实参列表);//如果需要传参需要加()来初始化
构建多个对象:new 类名对象的个数;
释放单个对象:delete 对象指针;
释放对象数组:delete [] 对象指针;
3)如何定义类中的数据成员
数据成员格式:const/static 数据类型 数据成员名;
数据成员:
普通数据成员:string name;
特点:主要有两部分组成 数据类型+数据成员名;
只可以被该类的对象所调用。每个对象都会为该数据成员存一份。
用户可以对其进行读或修改
初始化时可以通过普通构造对其初始化 也可以用初始化列表。
常数据成员:const string name;
特点:在类内或类外只能对其读 不可以对其做修改
初始化是必须用初始化列表对其初始化
静态数据成员:static string name;
特点:它属于类,同时也可以被该类的对象所调用,也可以通过类名进行直接调用
初始化:需要在类外对其初始化 一般不通过构造函数进行初始化。
必须初始化后才能使用。
4)如何定义函数成员
两种形式:类内实现;类内声明 类外实现
类内实现:如果在类内实现成员函数,系统会认为该成员函数时以个内置函数
类外的实现格式:返回类型 类名::成员函数名([形参列表]){}
成员函数的分类:
普通成员函数:int stu::add(int a,int b){}
特点:只能被该类 的对象所调用
常成员函数: int stu::add(int a,int b)const {}
特点:在常成员函数内 不可以修改类中的任何数据成员 只能读不能改
在该函数内不可以调用类中的非 常成员函数。
可以被常对象调用
静态成员函数 :static int stu::add(int a,int b){}
特点:可以被类和该类构建的对象所调用,它属于类
在该函数内 不可以调用类中的非静态成员函数
特殊成员函数:
构造函数: stu(){}
特点:主要初始化类中的数据成员
不需要用户主动调用
函数名必须和当前的类名一样,没有返回类型
析构函数:~stu(){}
:特点:主要做清理回收工作
不需要用户主动调用
函数名 ~+类名,没有返回类型,没有形参
5)成员的访问
数据成员访问:
类外: 对象名.数据成员名
类内:直接写数据成员名 或者 this->数据成员名
函数成员访问:类外: 对象名.函数成员(实参列表);
类内:直接写函数成员(实参列表) 或者 this->函数成员(实参列表);
6)访问范围修饰符
public:修饰的成员可以在类内和类外都可以被访问
protected:可以在当前类内或当前类的派生类内被访问
private:只能在当前的类内使用。
7)构造函数
作用:初始化类中的数据成员
stu::stu(实参列表){}
特点:1.没有返回类型 2,函数名必须和当前的类名一样 3.不需要用户主动调用 4.可以被重载
触发时机:当用户在构建一个对象时会被触发。
构造函数分类:
普通构造函数:使用最多
默认构造函数:没有形参的构造函数或者构造函数中的形参都是默认形参的构造函数
stu(){} stu(string name="sdf){}
拷贝构造函数:定义拷贝的规则,如果用户没有定义,系统会自动生成
stu(const stu&temp){}
触发时机:stu s1(s2) stu s1=s2;
对象做值传递
返回一个对象
8)析构函数
作用:释放资源或者清理回收工作
特点: 1)函数名= ~+类名 2)不需要用户主动调用 3)没有返回类型 4)没有形参
触发时机:栈对象是离开了它的作用域 对堆对象施加了delete
如果用户没有提供 系统也会自动生成。
10)动态对象的创建与释放
11)this指针
this是指向当前对象的一个指针,不可以被修改。
场景: 当成员函数内 局部变量与数据成员出现重名,可以使用该关键字加以区分
返回对象自身。
注意:只能在类中非静态成员函数内使用。
12)共用数据保护
const可以修饰对象:常对象 const stu s1; 意味着该类中的所有数据成员在类外都是只能读不能修改。
const可以修饰数据成员:
const可以修饰函数成员
18)静态成员
特点:静态成员属于类,可以通过类名或该类的对象来对其操作。
19)友元
友元函数 友元类
作用:与类之间实现资源共享 ,提供访问效率。
友元函数:
需要在类中先声明: friend 类型 函数名(形参列表);
类外实现:
特点:至少需要一个形参 并且是当前的类类型的引用类型。
友元类: friend class 类名;
注意:1,在那个类声明,就说明对那个类有所企图
2.友元函数不是类的真正成员,不可以通过对象来对其访问。
20)类的前项引用声明
使用类型的语句出现在定义类的前边,需要做引用声明
class 类名;
注意:只能以有限的方式使用(用指针或引用来使用它)。
21)在类中如何定义一组常量
通过枚举 有类型但是没有数据成员名
访问: 类名::枚举中的枚举值;
22)如何定义一个指针指向类中的成员函数
定义: 返回类型(类名::*成员函数指针名)(形参列表);
初始化:返回类型(类名::*成员函数指针名)(形参列表)=&类名::成员函数名;
使用:(对象名.*指针名)(实参列表);
23)匿名对象
构建:类名(实参列表);
匿名对象的生存期只限于在当前行。
24)C++中一个类会默认生成的6个特殊的成员函数:
默认构造函数:stu(){}
拷贝构造:stu(const stu&temp){全拷贝};
析构函数:~stu(){}
赋值运算符重装函数:stu&operator=(const stu&temp){}
移动构造函数:stu(stu&&temp){}
移动赋值: stu&operator=(stu&&temp){}
int x=a+b;
继承
c++
#include<iostream>
#include<string>
#include <stdio.h>
using namespace std;
class father
{
public:
string name;
int age;
father()
{
name="yangyang";
age=10;
}
void print_info()
{
cout<<"father method"<<endl;
}
};
//这个类就是派生类,public就是继承方式,father就是给派生类指定的父类名
//父类的构造函数和析构函数一般不能被派生类继承,因为派生类的成员比父类丰富,拿过来去初始化不合适。
//class son :public fahter,public mother{} 在定义派生类的时候,可以给派生类指定一个父类或多个父类。
//继承与派生:继承在强调派生类获得了父类的成员,可以为它所用,派生:强调的是在父类的基础上进行扩展和成员新增。
//派生来在对其指定父类时,除了要指定父类名外 还需要指定继承方式(设置派生类如何看待继承过来的成员)
//public,公有继承,protected 受保护继承 private:私有继承(默认)
class son:public father //son就是子类或派生类 father就是父类或基类
{
public:
void play_game() //如果父类没有,派生类可以去新增方法
{
cout<<" i can play hero union"<<endl;
}
};
int main()
{
son s1;//派生类通过继承,可以实现父类有的数据成员和成员函数可以直接拿来使用,而不需要自己重写实现。
cout<<s1.name<<endl;
s1.print_info();
s1.play_game();//派生类不仅复制了父类的几乎所有成员,同时也可以支配自己新增的成员,派生类比父类要更强大
}
c++
class father
{
public:
string name;
int age;
father()
{
name="yangyang";
age=10;
money=1000;
skill="driver";
}
void print_info()
{
cout<<"father method"<<endl;
}
protected :
string skill;
private:
int money;
};
//派生来在对其指定父类时,除了要指定父类名外 还需要指定继承方式(设置派生类如何看待继承过来的成员)
//public,公有继承,protected 受保护继承 private:私有继承(默认)
//派生类选择公有继承:在派生类中,继承过来的成员 仍然保留父类中原有的访问属性
//派生类选择私有继承:父类总原来公有的成员 则在派生类中变成私有成员,父类的受保护成员在派生类也是私有成员,父类的私有成员止步于父类,派生类无法使用。
//派生类选择保护继承:父类总原来公有的成员 则在派生类中变成受保护,父类的受保护成员仍然是受保护成员,私有成员,派生类无法访问。
class son:protected father
{
public:
void play_game()
{
cout<<" i can play hero union"<<name<<endl;
}
};
int main()
{
son s1;
//cout<<s1.name<<endl;
// s1.print_info();
// s1.play_game();
}
class father
{
public:
string name;
int age;
void print_info()
{
cout<<"father method"<<endl;
}
//在定义类的时候,一般需要将构造函数设置为公有,设置私有会造成无法再类外直接构建对象,同时也会影响到继承。
private:
father()
{
name="yangyang";
age=10;
}
};
c++
#include<iostream>
#include<string>
#include <stdio.h>
using namespace std;
class father
{
public:
string name;
int age;
father(string name,int age)
{
this->name=name;
this->age=age;
cout<<"父类的构造函数"<<endl;
}
void print_info()
{
cout<<"father method"<<endl;
}
~father()
{
cout<<"父类的析构函数"<<endl;
}
};
class son:protected father
{
public:
int sid;
//派生类的构造函数 既要对自身新增的数据成员提供必要的实参,还需要给父类的数据成员提供必要的数据。
son(string name,int age,int sid);
// {
//
// //this->sid=sid;
//
// }
~son()
{
cout<<"子类的析构函数"<<endl;
}
};
//派生类构造函数的定义格式: 类名::类名(形参列表):父类1(参数列表),父类2(参数列表),...数据成员名1(值1),数据成员名2(值2),...{}
//派生类构造在初始化数据成员时,先执行初始化列表,在执行构造函数体中的初始化语句,在执行初始化列表时,按照继承父类的顺序从左往右去初始化,和你的书写顺序没有关系
son::son(string name,int age,int sid):father(name,age),sid(sid)
{
cout<<"子类的构造"<<endl;
}
int main()
{
//派生类对象在销毁时,会先调用派生类的析构函数,最后再调用父类的析构函数。
son s1("yang",12,1001); //派生类对象在构建的时候,先调用父类的构造函数 后调用派生类的构造函数
}
c++
class father
{
public:
void fun()
{
cout<<"from father"<<endl;
}
};
class monther
{
public:
void fun()
{
cout<<"from monther"<<endl;
}
};
class son:public father,public monther
{
//public:
// void fun() //如果派生类中的成员和父类中的成员如何出现同名,此时派生类对象在调用该同名成员,则会优先调用派生类自身的。
// {
//
// cout<<"from son"<<endl;
// }
};
int main()
{
son s1; //二义性问题主要发生在多继承的场景中
//s1.fun();//当一个派生类继承多个父类,如果多个父类中存在相同的成员函数,此时 派生类对象在调用该方法时 这种访问的不缺点性就叫二义性问题
//3.在多继承中解决二义性问题的方法:1,就是在派生类中去重写多个父类中的同名成员函数
// s1.fun(); 2.派生类对象在调用这个同名的成员函数时,可以在函数名前加父类类名来做限定
s1.father::fun();
s1.monther::fun();
}
c++
class per
{
public:
string name;
void fun()
{
cout<<"from father"<<endl;
}
};
class son:public per
{
public:
int sid;
};
int main()
{
//c++中动态多态是靠继承章节中的赋值的兼容性来实现了,所以说,没有继承就没有多态。
per s1;
son s2;
//赋值的兼容性:父类类型时兼容派生类类型
s1=s2;//因为s2对象本身就是per类型,派生类对象可以给父类对象进行赋值,反之 则不可以
per*pt=&s2;
cout<<pt->name;
per&my=s2;
my.fun();
return 0;
}