目录
一、静态属性
本质:对象调用方法处理不属于对象的数据(之前学的是通过对象调用方法处理对象中的数据)
通俗的讲:基类中private中定义的属性不会继承到子类中,但子类创建的对象却想使用
#include<windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
using namespace std;
class Pet
{
public:
Pet(string theName);
~Pet();
static int getCount();
//作为一个接口,用于获取由其生成对象计数器的值
protected:
string name;
private:
static int count;
};
//子类
class Dog : public Pet
{
public:
Dog(string theName);
};
class Cat : public Pet
{
public:
Cat(string theName);
};
//分配内存+初始化为0
int Pet::count = 0;
/*Pet类*/
//构造器
Pet::Pet(string theName)
{
name = theName;
count++; //构造器被调用一次代表一个宠物出生,count+1
cout << "一只宠物出生了,名字为:" << name <<"\n\n";
}
//析构器
Pet::~Pet()
{
count--;
cout << name <<"死掉了" <<"\n\n";
}
//getCount方法
int Pet::getCount()
{
return count;
}
/*Dog*/
Dog::Dog(string theName) : Pet(theName)
{ }
/*Cat*/
Cat::Cat(string theName) : Pet(theName)
{ }
int main()
{
SetConsoleOutputCP(65001);
Dog dog("Tom");
Cat cat("Jerry");
cout << "\n已经诞生了" << Pet::getCount() << "只宠物" <<"\n";
{//划分了独立的区域
Dog dog_2("Tom2");
Cat cat_2("Jerry2");
cout << "\n现在呢,已经诞生了" << Pet::getCount() << "只宠物" <<"\n";
}
cout << "\n 还剩" << Pet ::getCount() <<"宠物!\n\n";
return 0;
}
释:子类创建的对象想访问父类中private下的哪个属性,哪个属性前加上static,变成静态属性,再在public下创建个接口(接口前加static),连通子类创建的对象和静态属性,以便对象使用
二、静态方法
注:
1.静态成员是所有对象共享的,不能在静态方法中访问非静态元素
2.非静态方法可以访问类的静态成员,也可以访问非静态成员
3.使用静态属性时,要为其分配内存
做法为:在类声明的外部对静态属性作出声明。( int Pet::count = 0; )
扩:内存分配方式
1.栈:存放局部变量和函数参数
2.堆:存放malloc分配的内存块
3.自由存储区:存放new来的内存块
4.全局\静态存储区:存放全局变量和静态变量
三、虚方法(基础特性之一)
问题引出:
子类dog、cat所调用的play方法不是覆盖后的play,而是基类Pet中的play
void Pet::play()
{
cout << name << " 正在玩儿! " <<"\n\n";
}
//覆盖
void Cat::play()
{
Pet::play();
cout << name << " 玩个球! " << "\n\n";
}
/覆盖
void Dog::play()
{
Pet::play();
cout << name << " 正在追杀猫! " <<"\n\n";
}
int main()
{
Pet *cat = new Cat("Tom");
Pet *dog = new Dog("Jerry"); //因为Cat、Dog是Pet的子类,所以可用Pet接收
cat->sleep();
cat->eat();
cat->play();//*
dog->eat();
dog->sleep();
dog->play();//*
delete cat;
delete dog;
return 0;
}
原因:
1.编译器追求速度
2.编译器在检查过程中,检查到cat、dog在编译时都是Pet类型,就理所当然的认为他俩调用的play方法就是Pet中的play方法,因为这样执行快
3.(根源)使用new在程序执行时才为cat和dog分配Cat和Dog类型的指针,而运行时才分配的类型和编译时的类型时不一样的
解决办法:
将play方法声明为虚方法
-做法:(+virtual) virtual void play();
注:
1.虚方法是集成的,在基类中将play方法声明为虚方法,就不能在子类中将play声明为非虚方法。
2.不确定要不要变成虚方法时,直接虚(因为无害)
3.多层次类的继承关系中,最顶级的基类应该全是虚方法
4.析构器就是虚方法。若不是虚方法,会导致内存泄漏。
(是虚方法时:当一个基类指针删除一个派生类对象时,派生类的析构函数可以被正确调用)
四、抽象方法:
virtual void play() = 0;//抽象方法 = 虚方法 + “ = 0 ”
告诉编译器,我不会在基类里实现play方法,别浪费时间了
五、多态(基础特性之一)
编译时的多态性 | 运行时的多态性 |
通过重载实现 | 通过虚函数实现 |
快 | 灵活、抽象 |
扩:
day8覆盖和day2重载区别:
覆盖 重载 范围 不同类(子类和基类) 同一个类 函数名 相同 相同 参数 相同 不同类型 *virtual* 基类中必须有 无所谓
六、运算符重载(基础特性之一)
方法:
定义一个重载运算符函数,在执行需要被重载的运算符时,系统自动调用该函数。
实质:
函数的重载
格式:
函数类型 operator 运算符名称 (形参)
{
重载处理
}
注:
1.c++不允许用户自己定义新运算符,所以只能对已有的进行重载
2.五个运算符不允许被重载:
. ——成员访问运算符
.*——成员指针访问运算符
::——域运算符
sizeof——尺寸运算符
?:——条件运算符
3.操作数个数、运算符的优先级别+结合性不能改变
欧几里得算法,参考:【算法】【欧几里得】数据结构与算法之欧几里得算法详解(附完整代码)-CSDN博客
class Rational
{
public:
Rational(int num,int denom);//num 分子,denom分母
Rational operator + (Rational rhs); //rhs==右侧参数,如a+b,rhs指b
void print();
private:
void normalize(); //简化分数
int numerator; //分子
int denominator; //分母
};
Rational::Rational(int num,int denom)
{
numerator = num;
denominator = denom;
normalize();
/*
1.若分母为负数,将负号转给分子
2.欧几里得算法(辗转求余)
*/
}
void Rational::normalize()
{
//确保分母为正
if(denominator < 0)
{
numerator = -numerator;
denominator = -denominator;
}
//欧几里得算法
int a = abs(numerator); //取绝对值
int b = abs(denominator);
//求最大公约数
while( b > 0)
{
int t = a % b; // 小对大取余
a = b;
b=t;
}
//分子、分母除以最大公约数得到最简化分数
numerator /= a ;
denominator /= a;
}
//重载 +
//a + b : a调用operator+ b
Rational Rational::operator+(Rational rhs)
{
int a= numerator;
int b = denominator;
int c = rhs.numerator;
int d = rhs.denominator;
int e = a*b +c*d;
int f = b*d;
return Rational(e,f);
}
int main()
{
SetConsoleOutputCP(65001);
Rational f1(2,16);
Rational f2(7,8);
//测试加法
Rational res = f1 +f2;
f1.print();
cout << " + ";
f2.print();
cout << " = ";
res.print();
cout <<"\n\n";
return 0;
}
七、重载操作符
ostream& operator<< (ostream& os, Rational f)
{
}
- os:将要向os写数据
- f:要写入的值
- 返回类型是ostream流的引用,一般来说,调用 operator<< 时,传递给他的是什么流,返回的就是那个流的引用
class Rational
{
public:
Rational(int num,int denom);
Rational operator + (Rational rhs);
private:
void normalize(); //简化分数
int numerator; //分子
int denominator; //分母
//new
friend ostream& operator<<(ostream& os, Rational f);
};
Rational::Rational(int num,int denom)
{
numerator = num;
denominator = denom;
normalize();
/*
1.若分母为负数,将负号转给分子
2.欧几里得算法(辗转求余)
*/
}
void Rational::normalize()
{
//确保分母为正
if(denominator < 0)
{
numerator = -numerator;
denominator = -denominator;
}
//欧几里得算法
int a = abs(numerator); //取绝对值
int b = abs(denominator);
//求最大公约数
while( b > 0)
{
int t = a % b; // 小对大取余
a = b;
b=t;
}
//分子、分母除以最大公约数得到最简化分数
numerator /= a ;
denominator /= a;
}
//重载 +
//a + b : a调用operator+ b
Rational Rational::operator+(Rational rhs)
{
int a= numerator;
int b = denominator;
int c = rhs.numerator;
int d = rhs.denominator;
int e = a*b +c*d;
int f = b*d;
return Rational(e,f);
}
//new 声明函数
ostream& operator<<(ostream& os, Rational f);
int main()
{
SetConsoleOutputCP(65001);
Rational f1(2,16);
Rational f2(7,8);
cout << f1 <<"\n\n";
cout << f1 << " + " << f2 << " == " << (f1+f2) <<"\n";
return 0;
}
//new 定义
ostream& operator<<(ostream& os, Rational f)
{
//只要插入符( << )后面的是Rational函数,就执行插入操作
os << f.numerator << "/" <<f.denominator;
return os;
}
总结:
1.重载操作符后的作用 = print
2.cout << f1;错误 因为f1是对象,不是变量
3.运算符重载、操作符重载还得多写
八、多继承
使用背景:
遇到的问题无法用“是一个”的关系来叙述时
例:有老师、学生两个类,A是一个老师,B是一个学生,
C既是学生又兼职补课老师——多继承
class Person
{
public:
Person(string theName);
void introduce();
protected:
string name;
};
class Teacher : public Person
{
public:
Teacher(string theName,string theClass);
void introduce();
void tech();
protected:
string classes;
};
class Student : public Person
{
public:
Student(string theName,string theClass);
void attendClass();
void introduce();
protected:
string classes;
};
//new 多继承
class TeachingStudent : public Student , public Teacher
{
public:
TeachingStudent(string theName,string classTeaching,string attendClass);
void introduce();
};
/*Person类*/
Person::Person(string theName)
{
name = theName;
}
void Person::introduce()
{
cout << " 大家好,我是 " <<name <<"\n\n";
}
/*Teacher类*/
Teacher::Teacher(string theName,string theClass) : Person(theName)
{
classes = theClass;
}
void Teacher::introduce()
{
cout << "大家好,我是" << name <<" 我教 " << classes <<"。" <<"\n\n";
}
void Teacher::tech()
{
cout <<name << " 教 " << classes <<"\n\n";
}
/*Student类*/
Student::Student(string theName,string theClass) : Person(theName)
{
classes = theClass;
}
void Student::attendClass()
{
cout << name << " 加入 " << classes <<" 学习。 " <<"\n\n";
}
void Student::introduce()
{
cout << "大家好,我是" <<name << " , 我在 " <<classes <<"学习。"<<"\n\n";
}
/*TeachingStudent类*/
TeachingStudent::TeachingStudent(string theName,string classTeaching,string attendClass)
: Teacher(theName, classTeaching) ,
Student(theName,attendClass)
{ }
void TeachingStudent::introduce()
{
cout << "大家好,我是" << Student::name << "。 我教" << Teacher::classes <<"\n\n";
cout << " 同时,我在" <<Student::classes <<"学习。"<<"\n\n";
}
int main()
{
SetConsoleOutputCP(65001);
Teacher tea("小甲鱼","C++快速入门");
Student stu("迷途者","C++快速入门");
TeachingStudent ts("嘿嘿","C++快速入门","C++进阶");
tea.introduce();
tea.tech();
stu.introduce();
stu.attendClass();
ts.attendClass();
ts.tech();
ts.introduce();
return 0;
}