C++面向对象编程速成

    科技2026-03-11  5

    看课程的笔记记录 课程来自:https://www.bilibili.com/video/BV1oK4y1s7Jd?from=search&seid=14170084346288815848

    1、什么是类(class),什么是对象

    c语言: int a=10 int表示整数类,这是系统自带的一个类,a则是这个类的一个对象 float,char都是系统自带的类 面向对象编程: 创建自己的类,创建自己的对象 比如:学生类studen,对象张三李四

    2、如何创建自己的类

    形式

    class student //这个大括号里的叫类定义 { string name; //学生的名字 int age; //学生的年龄 }

    类名:student 类里两个内容,年龄名字,叫做类的成员数据,或者属性

    3、公有和私有的初步概念

    在类的定义里,形式如

    class student { public: string name; private: int age; }

    name公有 类的外部(主函数里或者其他类)可以读写name的值 age私有 类的外部外面看不到 可以只有public 可以将名字和年龄都写在public,初学者可以先不写private 没有private的类相当于结构体

    4、如何使用一个类

    类被定义后,就创造类的对象

    student aa; //创建一个student类的对象,名字叫aa aa.age=20; //给aa的属性赋值,和结构体一样

    5、什么是成员函数

    成员函数的重载 和C语言普通函数一样,成员函数也可以重载 不同函数可以有相同的函数名,通过不同的输入参数来识别,如:

    class student { public int age; string name; bool set(int a); //两个函数名字一样,但输入格式不一样 bool set(string a); //这个函数的定义乐过,name=a,return ture; }

    比如aa.set(10)就调用了第一个,aa.(张三)就调用了第二个

    总结: 概念: 1、类(学生,类定义里要写成员和方法的声明) 2、对象(aa) 3、成员数据(年龄和名字,又叫属性) 4、成员函数(set,给年龄赋值,又叫方法,具体内容写在类的外面) 一个面向对象的代码最少有三个部分: 1、类的定义 2、成员函数的定义 3、创建对象然后调用方法

    6、构造函数

    student aa,创建aa对象,如果有构造函数,aa就有默认值 为什么要构造函数? 建立一个对象aa,要给他的年龄和姓名分别赋值,那就要写两行代 码set(“张三”);set(20),使用构造函数后,即使你有再多的成员数 据,不用一一写了 构造函数的本质即对象初始化 构造函数的名字必须和类的名字一样

    class student //类定义里先申明构造函数 { publicint age; string name; student() //这是构造函数,没有输入值和返回值,名字和类的名字一样 } student::student() //构造函数定义,特点就是冒号两边是一样的,注意这个函数没有输入值 { age=20; //构造函数里,可以对全部的成员数据给一个默认值 name="张三"; } //下面是主函数 student aa; //新建一个student类的对象,名字叫aa,这里不用再人工赋值年龄和名字了,将自动调用构造函数给年龄和名字默认值

    7、带参数的构造函数

    前面的构造函数没有输入值,年龄只能是20,改进一下

    class student //类定义里先申明构造函数 { publicint age; string name; student() //这是没参数的构造函数申明,允许几个名字一样的函数,通过不同输入值来区别 student(int a,string b); //同名的构造函数申明,即重载,带两个输入参数,即名字和年龄 } student::student(int a,string b) //这里带参数构造函数定义,大括号里是函数定义,前一个student代表类后一个代表构造函数 { age=a; //这样名字和年龄就不是默认值了,可以动态输入 name=b; } //下面是主函数 student aa; //新建一个student类的对象aa,自动给aa的属性填默认值 student bb(25,“李国”); //构造函数重载,新建一个student类的对象bb,同时动态输入名字和年龄

    8、析构函数

    析构函数销毁内存数据 对象aa被创建后,其数据就一直在内存中了,这块内存什么时候被 释放?就是使用析构函数的时候 但主函数中并不能直接运行student aa;delete aa; 只有student *p=new student(20,“张三”) delete p才将自动调用析构函数 写法和构造函数类似

    student::~student()//析构函数名字比构造函数多了一个波浪号,没有输入值,没有返回值 {cout<<"delete object";} //C++中cout相当于C语言中的printf就是打印

    9、常成员函数,关键字const

    一个方法只读取属性,而不修改属性,就是常成员函数

    class student; //类定义,这里省略了构造函数 { publicint age; string name; bool set(int i);//这里就是成员函数的声明,又叫方法 bool read() const;//后面加了const,表示这个函数只读不写 } bool student:read() const //常成员函数内容,后面加了const,只读不写 { cout<<name;//打印出姓名 cout<<age;//打印出年龄 return ture; } student aa;//创建对象 aa.read();//在创建aa后,调用read方法 实际可以不使用const,普通方法也可以读数据,加const只是安全起见,防止意外修改数据,更加不正规的做法就是直接读取:aa.age,在正规程序中还是会使用const来读取。

    10、静态成员函数,静态成员数据,关键字static

    静态成员数据 一个类可创建多个对象,student类可以不停新建aa bb cc dd对象 需要一个变量cnt=4来表示程序已经创建了4个对象了,cnt这个数,与student类有关,但又不属于aa bb cc中间任何一个对象 这种描述全局,又与某个对象属性无关的,叫做静态成员数据 读取静态成员数据的方法,叫静态成员函数

    在类定义中加入静态成员数据,静态成员函数

    class student;//类定义 { public: int age;//成员数据,年龄 string name;//成员数据,名字 student () ;//这是没参教的构造函数,创建对象时给age name默认值 static int cnt; // static表示静态成员数据,统计有多少个对象 static int count(); //static表示静态成员函数,返回有多少个对象 }

    ·如何定义静态成员:

    student :: student() //构造函激定义 { age=20//构造函数里,可以对全部的数据给一个默讯值 name=“张三”; Cnt = cnl +1//每次调用构造函数创建对象时,cnt+l,就可以统计出有多少对象了 } int student::count() ;//静态成员函数的定义,汪意这里不写static了 { return cnt; ,返回静态成员数据,统计占多少个对象 }

    如何使用静态成员函数,下面是主函数:

    student aa;//创建对象aa student bb;//再创建对象bb aa.count();//调用count方法,返回静态数据成员数据cnt=2,已建立两个对象,这行和下一行一样的 bb.count();//注意不论是aa还是bb,返回是一样的,静态成员不依赖某个对象 student::count{};//返回静态成员数据cnt=2,这个写法和aa.count()效果完全一样,只有不依赖与对象的静态函数才可以这么写

    总结: 1、普通成员函数 bool student::set(int a) 2、构造函数,自动初始化对象 student::student() 3、带参数的构造函数,动态初始化对象 student::student(int a,string b) 4、析构函数,销毁对象 student::~student() 5、常成员函数,只读不写 bool student::read() const 6、静态成员函数,依赖于类,但不依赖于某个对象 static int count()

    11、再谈谈public和private

    设private的目的 高手:对类的外部隐葳了类内部工作机制,即所谓“封装性” 新手: public的内容,可以在主函数看到,private内容只能在类的内部看到

    例子

    class student;//类定义,没写全,为了突出重点省略了构造函数和属性 { public://公有部分 void print name();//这个print名字是公有的方法 private;//私有部分 void print_age();//这个print年龄是私有的方法 } void student :: print_name()//定义print name公有方法 {cout<<“张三”;} void studcnt :: print_age()//定义print agc私有方法 {cout<<20;} //以下是主函数 student aa;//创建对象aa,构造函数我省略了 aa.print_name();//调用公有方法,打印出张三 aa.print_agc(;//调用私有方法,不允许,这行会报错,提示student类不存在print_age 这个方法

    既然私有方法不能被使用,那还有什么意义?可以用,有条件 核心思想:通过公有的方法的外壳调用私有方法

    class student;//类定义,没写全,为了突出重点省略了构造函数和属性 { public://公有部分 void print_age_public();//这个print_age_public名字是公有的方法,外面看得到 private;//私有部分 void print_age_private();//这个print_age_private是私有的方法,外面看不到 } void student :: print_age_public()//定义print_age_public公有方法 {print_age_peivate();}//重点来了,这里通过公有方法的外壳调用了私有方法 void studcnt :: print_age_private()//定义print agc私有方法 {cout<<20;} //以下是主函数 student aa;//创建对象aa,构造函数我省略了 aa.print_age_public();//这里通过公有函数的外壳,调用私有,然后打印出年龄20 aa.print_age_private(;//这行会报错,不允许直接调用私有方法

    12、正规程序的类定义写法

    之前讲的代码,以及之后讲的,都是属于“举例说明”,都是野路子的写法。(本页后无说明则仍全写在public中)

    正规写法:

    class student;//类定义 { public: bool set(int a);//两个函数名字一样,但输入格式不一样 bool set(string a); bool read() const;//后面加了const,表示这个函数只读不写 student(); student(int a,string b); private: int age; string name; }

    一般把“方法”全部放在public中 “属性”放在private中 目的是防止类的外部随便篡改数据 通过函数来修改数据时,一般都具有防错机制,满足了条件才让你改

    13、类的派生继承初步

    什么是派生 把student类,细分成本科生和研究生 本科生和研究生也是学生,是学生的子类,学生是父类,也叫基类或者超类 所谓派生,是相对于父类而言的;所谓继承,是相对子类而言的 派生出本科生的写法:

    class undergraduate : public student//本科生类定义,目号后面表示从student类公有派生而来 { public: string course: //这行新增定义了一个公开的字符申属性course,比如本科生的大学物理课程 }

    派生出研究生的写法:

    class postgraduate : public student// 研究生类定义,冒号后面表示从student类公有派生而来 { public: string research;//这行新增定义了一个和本科坐不一样的字符属性research,比如研究生的方向:芯片设计

    ●对比研究生本科生两个类: 都没有写两个基本属性name和age,因为根本不用写,子类自动继承父类的属性和方法 除了共有的name和age,也有不同的东西,这是非常重要的类的派生特性,除了继承来的,还能自己发展壮大,可以有自己的属性,也可以有自己的方法。 ●子类没有构造函数,他的对象怎么被创建? name和age 是否有值呢?答案是肯定的

    postgraduate bb : //创建研究生对象,此时子类无构造函数,系統将调用父类student的无参数构造函数给name和age赋一个默认值,其默认值在之前小节中,也就是”张三”,age=20 bb.set(25);//postgraduate类本身没有set方法,这调用了之前小节父类student的力法,对age赋值

    通过上面两行,说明了子类可以自动继承父类属性和方法

    14、类在不同情况下的继承

    public,private,protect 除了public和private,还有一个和他们平级的关键字,protect (保护)。 之前没有说protect,是因为在没有派生继承的情况下,protect与private效果完全一样

    研究生类定义: class postgraduate : public student {…} 为什么前面要加public这个字呢? public表示公有继承,只继承student的公有部分(age和name),并且age和name也将做为研究生类的public部分。研究生类是无法继承也无法访问student类的私有部分的。

    将父类student类定义的private替换为protect。对student本身无任何变化 当class postgraduate : public student … }时候,除了public被继承了,protect (私有)也被研究生继承了。student的protec t内容被继承到了postgraduate的protect中

    ●公有继承总结: 父类student, public内容A,private内容B,子类postgraduate公有继承,则只有public内容A 父类student, public内容A,protect内容B,子类postgraduate公有继承,则有public内容A+protect内容B ●私有继承和保护继承 class postgraduate : public student {…}。public替换成private利protect 父类public内容A,private内容B,子类私有继承,则子类只有内容A,且为private 父类public内容A,protect内容B, 子类保护继承,则子类有内容A B,且都为protect 在保密优先级上,private最私密, protect其次, public最公开。private 里的内容无论如何 都不能被继承

    15、子类的构造函数

    子类可以有自己的构造函数 子类没有构造函数,系统会调用父类student的构造函数 但是,不论子类有没有自己的构造函数,创建子类对象时,父类构造函数都将被调用

    研究生类不带参数构造:

    class postgraduate : public student //研究生类定义,表示从student派生而来 public: //研究生的公有成员 string research ; //这行新增定义了一个公开的属性research,宇符串,研究生的方向 postgraduate (); // 无参数的构造函数声明 postgraduate ( int a, string b, string c); //带参数的构造函数声明,参数为年龄姓名研究方向 postgraduate :: postgraduate()//无参数构造函数定义 { research “asic design” ; //对研究生的research属性赋值为asic design, 注意没有age和name //下面是主函数: postgraduate bb;// 创建研究生对象bb,调用无参数构造函数

    创建子类bb对象时,系统首先白动运行父类student构造函数,对age和Iname赋值, 随 后运行子类postgradiate构造函数,对bb对象进行拓展,增加research内容。 换句话说,子类是不能继承父类的构造函数的,只能调用父类构造函数。

    带参数的子类构造函数 定义和声明长的不一样,后面多了东西

    postgraduate :: postgraduate (int a, string b,string c) : student(a b) //带參数构造函数定义 {research - c;} //下面是主函数: . postgraduate bb( 25,“李四” ,“ASIC design") : //创建研究生对象bb,传入有参数的构造函数

    程序会先调用父类student构造函数,把25和李四两个值传入父类student带参数的构造函数,随后把参数c=asic design传给bb自己的research属性。这样实现了对研究生对象的全动态的初始化赋值。

    16、多态的初步

    方法真的是专属于某个类的么? 第一次介绍类的方法(成员函数)时说,方法是专属于某个类的,举了例子,int a=1; int b=2;则a+b=3; 加法专属于整数类的,如果是string a ; string b,显然是不能用加法的(char a +char b可以) 颠覆一下这个概念,在比较新的编程语言中,比如python/ruby, 两个字符串是可以执行加法的,形式如下: a = "xxxx” #这是python无需定义数据类型 b =“yyyy” print a+b 这里并不会报错,会打印出字符中“xxxxyyyy”,就是拼接了两个字符串

    这就是面向对象编程大名鼎鼎的“多态” 先给一个不太严谨的说法,其思想是,不同类的对象也可以用相同的方法,这里的相同指的是相同的方法名字,而方法里面的内容具体干了什么,是不一样的。 比如当a b是数字,符号”+”执行传统意义的加法,当a b.是字符串,符号”+”执行字符串拼接,系统将根据当前情况智能选择采用那种方法

    上面这个不严谨的说法,在python等新兴语言中是正确的,在C++中略有不同,C++比较古板,如果要.实现相同的函数名执行不同的内容,有3种方法: 重载、隐藏、覆盖(override,又叫重写,这个才是C++的多态)

    重载 python a+b这个例子,C++中看上去更接近重载 重载两个函数的参数格式必须是不一样的, 隐藏和覆盖的同名函数的参数可以完全一样 不依赖于面向对象,依赖编译器

    隐藏: 在父类student和 子类postgraduate中都定义一个函数study(),输入参数格式相同不同都可以

    class student//类定义, 没写全,为了突出重点省略了构逍函数和属性 {public: void study (bool a) {cout<< 好好学习”}; } class postraduate : public student//类定义, 没写全,为了突出重点省略了构造函数和属性 {public: void study (int b) {cout<<* 芯片设计”}; } //下面是主函数 postgraduate bb;//子类对象 student aa;//父类对象 bb.study(2); // 研究生对象调用研究生的study方法,参数为int,打印出芯片设计 aa.study(true)//学生对象调用学生的study方法,参数为bool, 打印出好好学习 bb.study( true ): //这行出错,研究生对象,但参数为bool,本来应该重裁父类的study方法, 因为这时父类方法被隐藏了,系统找不到对应的方法,这就是隐藏和重载的区别

    17、类的指针

    讲真正多态前必须先讲类指针 实际程序中,大量使用类指针;不考虑继承,类指针与结构体指针类似

    student *p: //新建一个student类指针 student aa; //新建aa对象 P=&aa; //p指针指向aa对象 p -> name ; //这个就和aa.name一样,返回aa的name值 p -> study(); //这个相当于aa.study(),对aa执行成员函数

    实际程序中使用更高级写法:

    student *p = new student:(20, ' 张三); delete p; //调用析构函数

    把继承考虑进去,复杂一点。类定义省略,且只考虑公有继承

    student *p1;//新建一个student父类指针 postgraduate *p2;//新建一个postgraduate子类指针 student aa;//新建父类对象aa postgraduate bb;//新建子类对象bb p1 = &aa;//父类指针指向父类对象,可以 p2=&bb;//子类指针指向子类对象,也可以,下面颠倒一下试试 p1 = &bb; //父类学生指针指向子类研究生对象,可以 p2 = &aa;// 子类研究生指针指向父类学生对象,这里报错

    父类指针是可以指向子类成员 因为研究生一定是学生的一种, 反过来则不行,学生可不一定都是研究生。 但p1 -> research 会报错,虽然父指针指向了postgraduate,但不能调用postgraduate中的属性和方法,父类指针抬向子类时,仍然只能用父类中的方法。.

    18、真正的多态与虚函数(覆盖),关键字virtual

    分别在student, undergraduate, postgraduate建一个方法 都叫study,但每个study学习内容是不一样的 在所有子类父类study声明前面加上关键字virtual,表示这是一个虚函数

    class student// 学生类定义,为了突出重点省略了构造函数和其他属性 { public: virtual void study ();//声明study方法,注意前面加了virtual,是个虚函数,并且没有参数 } void student :: study (); //注意这里没有virtual,长的和普通成员一样 {cout<<“好好学习”; } //学生类没有具体学的内容, 只有好好学习 //下而是研究生 class postgraduate : public student//研究生类定义, 为了突出重点省略了构造函数和属性,从student派生而来 { public://研究生的公有成员. virtual void study (); //声明study方法, 注意前面加了virtual,是个虚函教,也没有参数 } void postgreduate :: study () ;//注意这里没有virtual,长的和普通成员一样 {cout<< “芯片设计”; }//研究生学习芯片设计 //下面是本科生 class undergraduate : public student//本科生类定义,为了突出重点省略了构造函数和属性,从student派生而來 { public: //本科生的公有成员 virtual void study ();//声明study方法, 注意前面加了virtual,是个虚函数,也没有参数 } void undergraduate :: study () //注意这里没有virtual,长的和普通成员一样 { cout<<“大学物理”;}//本科生学习大学物理 在三个类中都设置了同名虚函数,并且他们的输入参数都是一样的也就是没有參数,这点和重载不同,下面是主函数 student aa; //学生对象aa postgreduate bb; //研究生对象bb undergraduate cc;//本科生对象cc student *p://定义父类指针,学生类指针 p=&aa;//指针指向学生对象 p -> study();//调用学生类的study方法,打印出好好学习. p=&bb;//父类指针指向子类对象 p -> study();//父类指针调用子类研究生的方法,打印出芯片设计,就是多态 p=&cc;//父类指针指向另一个子类对象 p -> study ();//父类指针调用了类本科生的方法,打印出大学物理,就是多态

    C++多态的核心,就是只要在程序开始时候设置一个父类指针,之后这个指针可以动态的指向不同的类,并且指针还可以动态的调用不同的类的方法,从而实现了不同数据类使用相同的方法,重载是编译时决定,多态是运行时决定

    19、纯虚函数与抽象类

    抽象类. 学生是一个类,研究生本科生或者中小学生都是学生类的子类 现实中,如果一个学生对象张三,那张三一定是研究生本科生或者中小学生中的一员 单纯只属于学生类而又不属于任何一个子类的对象张三在现实中是不存在的 学生类不应该具有自己的对象,于是学生类被叫做抽象类

    设置抽象类依赖纯虚函数,形式如下

    class student //学生类定义 { pub1ic: student ();//抽象类也是有构造函数的 student (int a,string b);//构造函数重载 virtual void study () -0;//声明study方法, 注意前而加了virtua1还有-0,表明这是纯虚函数 }

    当设置了virtual void study () =0;后 student就是一个抽象类 study ()函数只有声明,没有定义,其具体内容靠子类的多态来实现。 依然可以创建一个student的指针,但不能创建student对象了

    下面是主函数

    student aa; //这里报错,不允许创建抽象类对象 student *p; //可以创建抽象类指针 postgraduate bb; // 创建子类对象 p=&bb //父类指针指向子类 p -> study();//通过多态调用子类的study方法

    使用了抽象类后,父类student依然可以有年龄姓名等基本成员,这些成员是所有学生都有的,但父类没有了自己的study方法”好好学习”了,因为研究生本科生中小学生每个阶段都有自己的学习内容,所以具体是study函数内容,靠子类去具体实现。

    总结 ●公有和私有: 公有部分可以随便使用,私有函数和属性需要在类的内部通过公有外壳来调用。 ●类的派生与继承: 子类可以获得父类的成员和方法,并且还能增加自己的成员和方法。 ●类的继承public,privatc, protect分不同情况。 ●子类构造函数: 创建子类对象时,会先调用父类构造函数,随后再调用子类构造函数 ●实现相同函数名执行不同内容: 1重载,2隐藏,3覆盖(多态) ●什么是多态: 在父类子类所有函数声明都加virtual,并且定义父类指针 随着拍针拍向不同的对象,程序会自动找到不同类里的同名方法 ●什么是抽象类: 父类只是拥有一些共有的属性和方法,比如年龄姓名,但并不能创建自己的对象,创建对象依 赖子类实现

    Processed: 0.013, SQL: 9