C++篇——快速入门(day09)

本文详细介绍了C++中静态属性、静态方法、虚方法、抽象方法、多态以及运算符重载的概念,包括内存分配、多继承的应用实例,展示了如何通过这些特性实现面向对象编程的核心概念。
摘要由CSDN通过智能技术生成

目录

一、静态属性

二、静态方法

 注:

扩:内存分配方式

三、虚方法(基础特性之一)

问题引出:

原因:

 解决办法:

 注:

四、抽象方法:

五、多态(基础特性之一)

 扩:

六、运算符重载(基础特性之一) 

注:

七、重载操作符

 八、多继承

使用背景:


一、静态属性

本质:对象调用方法处理不属于对象的数据(之前学的是通过对象调用方法处理对象中的数据)

通俗的讲:基类中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方法,别浪费时间了

五、多态(基础特性之一

参考文章:C++:多态 详解_c++多态-CSDN博客

        编译时的多态性        运行时的多态性
        通过重载实现        通过虚函数实现
                快        灵活、抽象

 扩:

 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;
}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值