文章目录
一、面向对象
C++在C的基础上增加了面向对象编程
类是C++的特性,通常被称为用户定义的类型
类中的数据和方法称为类的成员,函数在类中也被称为类的成员
本质上是定义一个数据类型的蓝图,实际上并没有定义任何数据,只是定义了类的对象包含了哪些数据,以及可以在这个对象上执行哪些操作
三大特性 封装、继承、多态
封装
一个对象所封装的是自己的属性和方法,只保留一些对外的接口使之与外部发生联系
继承
已存在的类定义为基础,建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能
通过使用继承我们可以非常方便的复用以前的代码,能够大大提高开发效率
1、子类拥有父类 非private 属性和方法
2、父类可以拥有自己的属性和方法,子类可以对父类进行拓展
3、子类可以用自己的方式实现父类的方法
多态
多态就是值程序中的引用变量所指向的具体类型在编程的时候并不确定,但是在运行期间可以确定
因为在程序运行时才确定具体的类,不需要修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变
即不修改程序源码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性
二、类的定义和使用
//
// Created by KerwinWan on 2023/4/13.
//
#include "iostream"
using namespace std;
/*
* 类的定义时以关键字class开头,后面跟的是类的名字,类的主体是包含在一个{}中,类定义必须跟着分号或一个声明列表
* */
class Student{
// 关键字public 确定了成员的访问属性,在类对象作用域中,公共类在类的外部是可以访问的,也可以指定为projected 或 private
public:
string name;
int age;
double score;
void say(){
cout << "hello" << " " << name << endl;
}
};
int main(){
Student student1;
student1.name="zhangsan";
student1.age=18;
student1.score=90.1;
Student student2;
student2.name="lisi";
student2.age=1;
student2.score=92.1;
Student student3;
student3.name="wangwu";
student3.age=2;
student3.score=91.1;
student1.say();
student2.say();
student3.say();
return 0;
}
三、类的成员函数
类定义内部的函数,就像类定义中的其他变量一样,类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员
成员函数可以定义在类定义内部,或者类的外部使用范围解析运算符::来定义
class Student{
public:
string name;
int age;
double score;
void say(){
cout << "hello" << " " << name << endl;
}
const string &getName() const {
return name;
}
void setName(const string &name) {
Student::name = name;
}
int getAge() const {
return age;
}
void setAge(int age) {
Student::age = age;
}
double getScore() const {
return score;
}
void setScore(double score) {
Student::score = score;
}
};
class Student{
private:
string name;
int age;
double score;
public:
void say(){
cout << "hello" << " " << name << endl;
}
string getName();
void setName(string name);
int getAge();
void setAge(int age);
double getScore();
void setScore(double score);
};
// 也可以在外面实现类成员函数
void Student::setAge(int age) {
age=age;
}
void Student::setScore(double score) {
score=score;
}
void Student::setName(string name) {
name=nam;
}
string Student::getName() {
return name;
}
int Student::getAge() {
return age;
}
double Student::getScore(){
return score;
}
四、类访问修饰符
C++中类的所有成员的默认访问权限都是私有的private
结构体的所有成员的默认是公开的 public
公有成员在程序中类的外部是可以访问的
私有成员
成员和类的默认访问修饰符是private
私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的,只有类内和友元函数可以访问私有成员
保护(protected) 成员
保护成员变量或函数与私有成员十分相似,但是有一点不同,保护成员在派生类(子类)中是可以访问的
五、类的构造函数和析构函数
类的构造函数是类的一种特殊的成员函数,会在每次创建类的新对象时执行
构造函数可以用于为某些成员变量设置初始值
构造函数的名称与类的名称是完全相同的
并且不会返回任何类型
也不会返回void
public:
// 默认有一个空参构造
Student(){
cout << "构造了Student";
}
Student(string name,int age,double score){
this->name=name;
this->age=age;
this->score=score;
}
// 析构函数 每次对象销毁的时候调用 在构造函数前面加个~就是析构函数 没有参数和返回值
// 一个类有且仅有一个析构函数,如果定义的时候没有写析构函数,则默认生成析构函数
// 析构函数在对象消亡的时候自动被调用
// 如果用new创建对象,那么消亡不会被自动调用,只有调用delete语句 才会被释放
~Student(){
cout << "Student 被销毁了"<< endl;
}
Student *student=new Student;
delete student;
常量初始化和引用初始化的时候 必须赋值,或者就使用初始化列表符合
class Student{
private:
Student(string name, int age, double score);
string name;
int age;
double score;
};
Student ::Student(string name,int age,double score):name(name),age(age),score(score){
cout << name << endl;
}
六、this指针
this 是C++ 的一个关键字 同时也是一个const指针,指向当前对象,通过它可以访问当前对象的所有成员
Student(string name,int age,double score){
this->name=name;
this->age=age;
this->score=score;
}
七、友元
友元的目的是在类的成员函数外直接访问对象的私有成员
友元函数是定义在类外部,可以访问该类中的所有私有成员和保护成员,指定函数为某个类的友元函数的方法是使用关键字friend
语法
friend <返回类型><函数名>(<参数列表>);
//
// Created by KerwinWan on 2023/4/13.
//
#include "iostream"
using namespace std;
class Box;
class BigBox{
// 因为BigBox类是Box类的友元类,所以可以直接访问该类的任何成员
public:
void Print1(int width,Box &box);
};
class Box{
int width;
public:
// 全局友元函数
friend void printwidth(Box box);
// 友元类
friend class BigBox;
// 友元成员变量
friend void BigBox::Print1(int width,Box &box);
void setWidth(int wid){
this->width=wid;
}
};
void BigBox::Print1(int width,Box &box){
box.setWidth(width);
cout << box.width <<endl;
}
void printwidth(Box box){
// peintwidth是Box的友元,所以可以直接访问该类的任何成员
cout << box.width << endl;
}
int main(){
Box box;
BigBox bigBox;
// 成员函数设置参数
box.setWidth(10);
// 使用友元函数输出宽度
printwidth(box);
// 使用友元类的方法设置宽度
bigBox.Print1(20,box);
return 0;
}
八、指向类的指针
//
// Created by KerwinWan on 2023/4/17.
//
#include "iostream"
using namespace std;
class Box{
private:
double length;
double width;
double height;
public:
Box(double length,double width,double height){
cout << "构造函数被调用啦" << endl;
this->length=length;
this->width=width;
this->height=height;
}
double getV(){
return length*width*height;
};
};
int main(){
Box box1(1,2,3);
Box box2(4,5,6);
Box *p=&box1;
// 如果是指针 点运算不可以用 用->
cout << "体积是:" << p->getV() << endl;
p=&box2;
cout << "体积是:" << p->getV() << endl;
return 0;
}
九、静态成员变量static
通常使用static关键字把类成员定义为静态的
当我们声明类的成员为静态时,意味着无论创建多少个类的对象,静态成员都只有一个副本
静态数据成员初始化的格式如下:
数据类型 类名 :: 静态数据成员名=值
初始化在类外进行,前面不加static
静态方法不能直接调用一般成员
静态方法调用的是 类名::方法名(参数)
//
// Created by KerwinWan on 2023/4/17.
//
#include "iostream"
using namespace std;
class Box{
private:
double length;
double width;
double height;
public:
// 定义静态变量
static int count1;
Box(double length,double width,double height){
cout << "构造函数被调用啦" << endl;
this->length=length;
this->width=width;
this->height=height;
count1++;
}
static double getSV(Box &box);
double getV(){
return length*width*height;
};
};
// 对静态变量进行初始化
int Box::count1=0;
double Box::getSV(Box &box) {
return box.height*box.width*box.length;
}
int main(){
Box box1(1,2,3);
Box box2(4,5,6);
Box *p=&box1;
// 如果是指针 点运算不可以用 用->
cout << "体积是:" << p->getV() << endl;
p=&box2;
cout << "体积是:" << p->getV() << endl;
// 调用静态变量进行输出
cout << Box::count1 << endl;
cout << Box::getSV(box1) << endl;
return 0;
}
十、继承
继承是代码可以复用的最重要的手段
允许程序员在保持原有类特性的基础上进行拓展,增加功能,产生新类,也就是派生类,被继承的类称为基类
继承的语法
class 新类的名字:继承方式 继承类的名字{};
当一个类派生自基类,该基类可以被继承为public、protected和private类型
几乎不使用protected和private继承,通常使用public继承
public继承:父类成员在子类中保持原有访问级别
private继承:父类成员在子类中变为private成员
protected继承:父类中public成员变为protected protected成员仍为protected成员 private成员仍为private成员
private成员在子类中依然存在但是无法被访问
不论哪种继承都不能直接使用父类的私有成员
如果父类有多个构造函数,子类需要实现与父类相应的多个构造函数,并且是通过初始化列表的方式(类名后面加冒号)
因为创建子类的过程:1、实例化父类 2、实例化子类
//
// Created by KerwinWan on 2023/4/17.
//
#include "iostream"
using namespace std;
class Parent{
private:
int a;
protected:
int b;
public:
int c;
Parent(){
a,b,c=0,0,0;
}
};
class Son1:public Parent{
public:
void print(){
// 私有成员不可以直接被访问
cout << b << endl;
}
};
class Son2:private Parent{
public:
void print(){
cout << b << endl;
}
};
class Son3:protected Parent{
public:
void print(){
cout << b << endl;
}
};
int main(){
Son1 son1;
Son2 son2;
Son3 son3;
cout << son1.c << endl;
// cout << son2.c << endl; 不可以访问projected
// cout << son3.c << endl; 不可以访问projected
return 0;
}
子类重写父类的方法
//
// Created by KerwinWan on 2023/4/17.
//
#include "iostream"
using namespace std;
class Parent{
private:
char c;
int a;
public:
Parent(char _c,int _a){
c=_c;
a=_a;
}
void dispaly(){
cout << c << " " << a << endl;
}
};
class Son:public Parent{
private:
int score;
public:
Son(int score,char c,int a): Parent(c,a){
this->score=score;
}
void display(){
// 重写方法的同时 调用父类的方法访问私有成员
cout << score << endl;
Parent::dispaly();
}
};
int main(){
Son son(1,'a',3);
son.display();
return 0;
}
多继承
/*
* C++还支持多继承
*/
class Son:public Parent,public ParentA,private ParentB{
private:
int score;
public:
Son(int score,char c,int a): Parent(c,a){
this->score=score;
}
void display(){
// 重写方法的同时 调用父类的方法访问私有成员
cout << score << endl;
Parent::dispaly();
}
};
虚基类解决二义性
虚基类:父类对祖父类的继承方式改为虚继承,那么子类访问自己从祖父类那里继承过来的成员就不会有二义性,子类对象里的祖父类对象统一为一个,继承的只有一个祖父类对象
//
// Created by KerwinWan on 2023/4/18.
//
#include "iostream"
using namespace std;
class Grandpa{
public:
int year_old;
void show(){
cout << year_old << endl;
}
};
class Parent_f:virtual public Grandpa{};
class Parent_m:virtual public Grandpa{};
class Son:public Parent_f,Parent_m{};
int main(){
Grandpa grp;
Parent_f pa_f;
Parent_m pa_m;
Son son;
grp.year_old=100;
pa_f.year_old=33;
pa_m.year_old=30;
son.year_old=10;
grp.show();
pa_f.show();
pa_m.show();
son.show();
return 0;
}
十一、多态
C++中的多态分为动态多态和静态多态
静态多态在编译阶段实现,原理是函数重载
动态多态通过虚函数实现
动态多态两个必要条件
1、必须通过基类的指针或者引用
2、被代用的必须是虚函数
在父类的函数前加上virtual关键字,在子类中重写该方法,运行时将会根据对象的实际类型来调用相应的函数
如果对象类型是派生类,就调用派生类的函数
如果对象类型是基类,就调用基类的函数
多态的时候 父类要先于子类构造,子类析构完成 父类才可以析构
父类通过虚函数才可以调用子类函数
协变:当基类和派生类中该函数的返回值为父子关系的指针或引用,返回值可以不同
//
// Created by KerwinWan on 2023/4/18.
//
#include "iostream"
using namespace std;
class Parent{
public:
virtual void func1(int val){
cout << "Parent-->func" << val << endl;
}
virtual void test(){
cout << "Parent->test" << endl;
}
void show(){
cout << "Parent->show()方法被调用" << endl;
}
};
class Son:public Parent{
public:
virtual void func1(int val){
cout << "Son->func" << val << endl;
}
void show(){
cout << "Son->show()方法被调用" << endl;
}
};
int main(){
Parent *p=new Son;
p->show(); // 普通自身调用
p->test();
p->func1(123);
return 0;
}