翔安区建设局网站,网站服务器计算机安全的措施,公司名称注册规定,东莞推广系统电话面向对象1.面向对象编程(难点)2.类和对象demo1:地主类的实现版本1demo2:地主类的实现版本23.访问修饰符demo3:外部修改成员变量不安全(版本3)demo4: 使用封装防止直接修改成员变量#xff08;版本3#xff09;demo5:进一步封装#xff1a;设置/获取名字#xff0c;修改积分…
面向对象1.面向对象编程(难点)2.类和对象demo1:地主类的实现版本1demo2:地主类的实现版本23.访问修饰符demo3:外部修改成员变量不安全(版本3)demo4: 使用封装防止直接修改成员变量版本3demo5:进一步封装设置/获取名字修改积分版本44.构造函数与析构函数(重点)4.1默认构造函数demo6: 使用构造函数进行成员变量的初始化4.2 带参构造函数demo7:学生类构建--堆内存对象/栈内存对象4.3 析构函数demo8:学生类析构函数演示5.this指针(重点)demo9:学霸返回引用的使用《老九学堂C课程》《C primer》学习笔记。《老九学堂C课程》详情请到B站搜索《老九零基础学编程C入门》-------------简单的事情重复做重复的事情用心做用心的事情坚持做(老九君)---------------1.面向对象编程(难点)
oop: object oriented programming
何为面向对象基于对象的概念以对象为中心以类和继承为构造机制来认识、理解、刻画客观世界涉及构建相应的软件系统模拟现实
1.对象–有数据和容许的操作组成的封装体与客观实体有直接的对应关系属性和方法的集合
面向对象不是某一种语言的特性而是一种编程思想。原来面向过程代码超过10W行就会难管理原来有因为飞机控制程序中一个写成.号造成的空难。
举个粒子斗地主游戏的开发 面向过程一步一步来很多很多的过程函数开始游戏-洗牌-发牌-显示手牌…-输出结果
面向对象 1.游戏参与者行为模式是相同的–玩家对象相同的属性和行为 2.进行游戏的场景–牌桌对象负责现实游戏的界面及内容 3.游戏规则系统–裁判对象负责判定牌面、输赢
小结 1.面向过程编程首先考虑要遵循的步骤然后考虑如何表示这些数据 2.oop编程首先会考虑数据包括数据的表示和数据的使用
2.类和对象
面向对象的编程流程 1.抽象从具体食物抽取共同的本质特征【处理复杂问题的技巧–简化、抽象】 地主对象外表特征–胖留两撇胡子两颗大金牙行为特点–先出牌多摸三张牌 2.用类封装将抽象转换为用户定义类型的工具将数据表示和操作数据的方法组合成一个整体。类的实例成为对象(对象的集合就是类还可以这么理解类就是对象模版)类中的变量和函数称为成员。 地主类 成员变量名称、积分、手牌 成员函数摸牌、出牌、产看积分 类的声明使用class/struct 关键字声明 两者的区别使用class声明的类默认成员是私有的privatestruct声明的类默认成员是共有的public。 推荐使用class声明类struct声明结构只包含数据POD老式数据 class 类名{}; struct 类名{}; 头文件中声明类(.h/.cpp)专门有一个类名.cpp文件实现类。
demo1:地主类的实现版本1
–LandOwnerV1.cpp 文件中既声明又实现包含main 函数的main.cpp文件中调用
// mian.cpp 文件
#include LandOwnerV1.cpp
int main(){// 类对象实验LandOwnerV1 landOwner1; // 声明了一个LandOwner1类型的变量landowner1// 调用对象的成员方法, 不能直接使用对象的私有成员// landOwner1.cards[0] 0; cards is a private member of LandOwnerV1 直接报错landOwner1.TouchCard(100);return 0;
}// LandOwnerV1.cpp 文件既声明又实现
#include iostream
using namespace std;
// .hpp 一般包含实现的内联函数通常用于模版类这种声明与实现共存的情况
// 建议只要不是纯模版一律使用.h 作为头文件后缀 .cpp 作为函数的实现文件
// 地主类的声明、实现
class LandOwnerV1 {private:string name; // 名称long score; // 积分int cards[20]; // 手牌数组public :LandOwnerV1() {}; // 默认构造函数~LandOwnerV1() {}; // 默认析构函数void TouchCard(int CardCount){// 暂时省略函数实现cout name 摸了 CardCount 张牌 endl;}void ShowScore(){cout name 当前的积分为 score endl;}};输出
摸了100张牌demo2:地主类的实现版本2
–LandOwnerV2.h中声明LandOwnerV2.cpp中实现main.cpp文件中调用
成员函数没有调用成功
// mian.cpp 文件
#include LandOwnerV2.h // 关注.h 文件
using namespace std;
int main(){LandOwnerV2 landowner2;landowner2.name 小明;//landowner2.TouchCard(20); // 这个方法实现不了cout landowner2.name endl;return 0;
}
// LandOwnerV2.h
#include iostream
using namespace std;
// .hpp 一般包含实现的内联函数通常用于模版类这种声明与实现共存的情况
// 建议只要不是纯模版一律使用.h 作为头文件后缀 .cpp 作为函数的实现文件
// 地主类的声明class LandOwnerV2 {private:long score; // 积分int cards[20]; // 手牌数组public :string name; // 名称LandOwnerV2(); // 构造函数声明~LandOwnerV2(); // 析构函数声明 void TouchCard(int); // 声明摸牌函数void PlayCard(int); // 声明出牌函数void ShowScore(); // 声明产看积分函数
};// LandOwnerV2.cpp
#include iostream
#include LandOwnerV2.h
using namespace std;LandOwnerV2::LandOwnerV2()
{//ctor
}void LandOwnerV2::TouchCard(int CardCount){cout name 摸了 CardCount 张牌 endl;
}
void LandOwnerV2::ShowScore(){cout name 当前的积分为 score endl;
}LandOwnerV2::~LandOwnerV2()
{//dtor
}(xcode 太不友好了写到类分文件时就无法编译弃坑转向CLION友好很多。)
3.访问修饰符
public: 修饰的成员在任意地方都可以访问 private:修饰的成员只能在类中或者友元函数中访问, 私有属性可以习惯性在名字前面加一个_ protected:修饰的成员可以在类中函数、子类函数、友元函数中访问
数据隐藏不希望别人随意操作,对应的操作叫做封装
在修饰关键字放在类定义的关键字中加冒号无修饰关键字默认为private
class 类名{
修饰符:成员类标;
};demo3:外部修改成员变量不安全(版本3)
非私有成员会被直接修改–修改地主积分
// mian.cpp 文件
#include iostream
#include LandOwnerv3.h
using namespace std;
int main(){// 访问修饰符的实验LandOwnerv3 landOwner3;landOwner3.name 巴依老爷;// 修改地主积分landOwner3.score 100;landOwner3.ShowScore(landOwner3.score);return 0;
}// LandOwnerv3.h 文件
// Created by 陈莹莹 on 2021/1/28.
#include iostream
#ifndef HELLOWORLD_LANDOWNERV3_H
#define HELLOWORLD_LANDOWNERV3_H
using namespace std;
class LandOwnerv3 {int cards[20]; // 手牌数组
public :string name;long score; // 积分LandOwnerv3(); // 构造函数声明 没有返回值~LandOwnerv3(); // 析构函数声明void TouchCard(int); // 声明摸牌函数void PlayCard(int); // 声明出牌函数void ShowScore(int); // 声明产看积分函数
};
#endif //HELLOWORLD_LANDOWNERV3_H
// LandOwnerv3.cpp 文件
// Created by 陈莹莹 on 2021/1/28.
// 用来演示封装的基本概念
#include iostream
#include LandOwnerv3.h
using namespace std;
LandOwnerv3::LandOwnerv3()
{//ctor
}
void LandOwnerv3::ShowScore(int score){cout name 当前的积分为 score endl;
}LandOwnerv3::~LandOwnerv3()
{//dtor
}输出
巴依老爷当前的积分为100demo4: 使用封装防止直接修改成员变量版本3
但是如此操作使得大家可以随意修改这个积分有一些不合理的积分就会出现。为了解决积分被赋值为不合理的情况需要将成员变量score进行封装使用方法来实现对成员的封装 demo4:类封装概念get/set方法
// mian.cpp 文件
#include iostream
#include LandOwnerv3.h
using namespace std;
int main(){// 访问修饰符的实验LandOwnerv3 landOwner3;landOwner3.name 巴依老爷;// 修改地主积分landOwner3.SetScore(-100);landOwner3.ShowScore();return 0;
}
// LandOwnerv3.h 文件
// Created by 陈莹莹 on 2021/1/28.
#include iostream
#ifndef HELLOWORLD_LANDOWNERV3_H
#define HELLOWORLD_LANDOWNERV3_H
using namespace std;
class LandOwnerv3 {long score; // 积分int cards[20]; // 手牌数组
public :string name;LandOwnerv3(); // 构造函数声明 没有返回值~LandOwnerv3(); // 析构函数声明void TouchCard(int); // 声明摸牌函数void PlayCard(int); // 声明出牌函数void ShowScore(); // 声明产看积分函数// 定义成内联函数即可void SetScore(long lScore){// 通过条件判断封装了score的赋值过程if(lScore 0){score 0;}else{score lScore;}}
};
#endif //HELLOWORLD_LANDOWNERV3_H// LandOwnerv3.cpp 文件
// Created by 陈莹莹 on 2021/1/28.
// 用来演示封装的基本概念
#include iostream
#include LandOwnerv3.h
using namespace std;
LandOwnerv3::LandOwnerv3()
{//ctor
}
void LandOwnerv3::ShowScore(){cout name 当前的积分为 score endl;
}LandOwnerv3::~LandOwnerv3()
{//dtor
}输出
巴依老爷当前的积分为0demo5:进一步封装设置/获取名字修改积分版本4
// mian.cpp 文件
#include iostream
#include LandOwnerv41.h
using namespace std;
int main(){// 访问修饰符的实验LandOwnerv41 landOwner4;landOwner4.SetName(巴依老爷);cout landOwner4.GetName() endl;// 修改地主积分landOwner4.SetScore(-100);landOwner4.ShowScore();return 0;
}// LandOwnerv41.h 文件
// Created by 陈莹莹 on 2021/1/29.
#ifndef HELLOWORLD_LANDOWNERV41_H
#define HELLOWORLD_LANDOWNERV41_H#include iostream
using namespace std;
class LandOwnerv41 {long score; // 积分int cards[20]; // 手牌数组string name;
public :LandOwnerv41(); // 构造函数声明 没有返回值~LandOwnerv41(); // 析构函数声明void TouchCard(int); // 声明摸牌函数void PlayCard(int); // 声明出牌函数void ShowScore(); // 声明产看积分函数// 定义成内联函数即可void SetScore(long lScore){// 通过条件判断封装了score的赋值过程if(lScore 0){score 0;}else{score lScore;}}string GetName(){return name;}void SetName(string lName){name lName;}};
#endif //HELLOWORLD_LANDOWNERV41_H// LandOwnerv41.cpp 文件
// Created by 陈莹莹 on 2021/1/29.
#include LandOwnerv41.h
#include iostream
using namespace std;
LandOwnerv41::LandOwnerv41()
{//ctor
}
void LandOwnerv41::ShowScore(){cout name 当前的积分为 score endl;
}LandOwnerv41::~LandOwnerv41()
{//dtor
}4.构造函数与析构函数(重点)
构造函数与类同名没有返回值 构造函数的作用给编译器看的编译器在对象被创建时为对象分配内存空间并自动调用构造函数以完成成员的初始化 构造函数的种类无参数构造一般构造重载构造拷贝构造。本节主要介绍无参构造一般构造
4.1默认构造函数
demo6: 使用构造函数进行成员变量的初始化
// mian.cpp 文件
//演示构造函数
#include LandOwnerv41.h
using namespace std;
int main(){LandOwnerv41 landOwnerv4; // 省略了默认构造LandOwnerv41 landOwnerv4(); // 标准写法但是不调用默认构造函数了--不输出内容了return 0;
}// LandOwnerv41.h 文件
// 与demo5一致// LandOwnerv41.cpp 文件
// Created by 陈莹莹 on 2021/1/29.
//
#include LandOwnerv41.h
#include iostream
#include memory.h
using namespace std;
LandOwnerv41::LandOwnerv41()
{cout LandOwnerV41的无参数构造函数(默认构造)被调用 endl;name 默认地主;score 0;//将用户的手牌数组初始化为0memset(cards, 0, sizeof(cards)/sizeof(cards[0]));cout 初始化的结果如下 endl;cout 名字 name endl;cout 积分 score endl;cout 手牌数组 ;for(int i0; i sizeof(cards)/sizeof(cards[0]); i){cout cards[i] \t;}cout endl;
}
void LandOwnerv41::ShowScore(){cout name 当前的积分为 score endl;
}LandOwnerv41::~LandOwnerv41()
{//dtor
}为啥手牌数组的初始化不是全为0
LandOwnerV41的无参数构造函数(默认构造)被调用
初始化的结果如下
名字默认地主
积分0
手牌数组0 0 0 0 0 0 0 0 -375207560 32766 -375207584 32766 0 1 0 0 0 0 0 0 默认构造显式写法 LandOwnerV4() default; 注意 1.如果创建的类中未书写任何构造函数系统会自动生成默认的无参构造函数函数为空什么都不做 2.如果书写了构造函数系统不会自动生成默认构造函数如果希望有一个这样的无参数构造函数需要自己显示书写LandOwnerV4() default;
4.2 带参构造函数
语法
类名::构造(类型1 参数1, 类型2 参数2, ....){// 相关初始化方法
}demo7:学生类构建–堆内存对象/栈内存对象
1.栈内存中的对象类似于函数声明 具体对象由系统创建并释放不用担心内存泄露。 声明周期只在声明区域的大括号内。 栈内存的优势存取速度快仅次于寄存器缺点栈内存中的数据大小生存期是确定的缺乏灵活性。 自定义类型名 对象名 Student stu(); Student stu; 2.堆内存中的对象需要new关键字 p_stu1是指针必须使用delete释放 使用灵活可以赋给全局变量可以把对象作为函数的返回值 用好了 Student *p_stu1 new Student(); Student *p_stu2 new Student(); auto *p_stu3 new Student(); // auto 自动类型判断不推荐使用sizeof 会报错 类对象推荐放在堆内存中即使用new来创建对象。
// mian.cpp 文件
#include iostream
#include Student.h
using namespace std;
int main(){// 演示构造函数,实质就是函数重载Student stu1;Student stu2(马化腾,普通家庭); // 在栈内存中直接分配空间速度块// 如果构造函数中只有一个参数且单参数构造函数只有一个可以直接使用赋值操作进行初始化// Student stu4 45stu2.showInfo();// 学生指针类型, 使用new 分配内存空间Student * stu5 new Student(杰克马, 毁创阿里); // 在堆内存中分配空间// 类指针访问方法要使用 -stu5 -showInfo();return 0;
}输出结果
默认构造函数
设置带参构造
普通家庭马化腾
设置带参构造
毁创阿里杰克马// Student.h 文件
//
// Created by 陈莹莹 on 2021/2/2.
//
#ifndef HELLOWORLD_STUDENT_H
#define HELLOWORLD_STUDENT_H
#include iostream
using namespace std;class Student {
private:string m_name;string m_desc;int m_age;
public:Student(); // 默认构造函数Student(string, string);~Student();void showInfo();};
#endif //HELLOWORLD_STUDENT_H// Student.cpp 文件
//
// Created by 陈莹莹 on 2021/2/2.
//
#include iostream
#include Student.h
using namespace std;Student::Student() {cout 默认构造函数 endl;
}
//Student::Student(string name, string desc){
// // 参数列表不同函数重载
// m_name name;
// m_desc desc;
// cout 设置带参构造 endl;
//}
//初始化参数列表的写法和上上面的带参数构造方法完全相同
Student::Student(string name, string desc):m_name(name), m_desc(desc){cout 设置带参构造 endl;
};void Student::showInfo() {cout m_desc m_name endl;
}
Student::~Student() {}4.3 析构函数
对象销毁时自动调用的特殊成员函数 析构函数一般用来完成清理工作。 析构函数名称在类名前加一个析构函数没有参数只能有一个
在栈内存中构造的对象在栈区mian函数销毁时类对象会被自动销毁。在堆内存中构建的类对象需要手动释放。
在析构函数内部需要释放(delete)掉在构造函数中手动分配(new)的空间对象内部的成员变量。
1.析构函数用来释放对象使用的资源并销毁对象的非static数据成员. 2.无论何时一个对象被销毁都会自动调用析构函数隐式析构
demo8:学生类析构函数演示
类对象推荐放在堆内存中即使用new来创建对象。
// mian.cpp 文件
#include iostream
#include Student.h
using namespace std;
int main(){// 演示构造函数,实质就是函数重载Student stu1;Student stu2(马化腾,普通家庭); // 在栈内存中直接分配空间速度块// 如果构造函数中只有一个参数且单参数构造函数只有一个可以直接使用赋值操作进行初始化// Student stu4 45stu2.showInfo();// 学生指针类型, 使用new 分配内存空间Student * stu5 new Student(杰克马, 毁创阿里); // 在堆内存中分配空间// 类指针访问方法要使用 -stu5 -showInfo();return 0;
}输出结果
默认构造函数
设置带参构造
普通家庭马化腾
设置带参构造
毁创阿里杰克马
杰克马被释放 # 杰克马为啥被释放了
马化腾被释放
被释放// Student.h 文件
// 与demo7一样// Student.cpp 文件
//
// Created by 陈莹莹 on 2021/2/2.
//
#include iostream
#include Student.h
using namespace std;Student::Student() {cout 默认构造函数 endl;
}
//Student::Student(string name, string desc){
// // 参数列表不同函数重载
// m_name name;
// m_desc desc;
// cout 设置带参构造 endl;
//}
//初始化参数列表的写法和上上面的带参数构造方法完全相同
Student::Student(string name, string desc):m_name(name), m_desc(desc){cout 设置带参构造 endl;
};void Student::showInfo() {cout m_desc m_name endl;
}
Student::~Student() {cout m_name 被释放 endl;
}5.this指针(重点)
类似于于python中的self,在python中显式传递self参数 每个成员函数包括构造函数和析构函数都有一个this指针。this指针指向调用对象可以通过this关键字访问当前对象的成员。
访问成员变量this - 成员名;访问成员函数this - 函数名();
this 在成员函数执行前就已经创建在析构函数之后被销毁。
-this 指针的类型为*const,为右值可以赋值 -this指针本身不占用大小并不是对象的一部分用sizeof 测不出来大小 -this指针作用域在成员函数内部 -this指针是类成员函数的第一个默认隐含参数编译器自动维护传递编写者不能显式传递。 -在类非静态成员函数中才可以使用this指针其他任何函数都不可以static是先于类实例存在的this还没有创建
this 返回当前对象的引用太难用了-- Student GetSuperScholars(Student );
demo9:学霸返回引用的使用
//mian.cpp
#include iostream
#include Student.h
using namespace std;
int main(){Student *ptr_stu1 new Student(迪丽热巴,微胖女孩);ptr_stu1-AddScore(78.9);ptr_stu1-AddScore(77.9);ptr_stu1-AddScore(72.9);ptr_stu1-AddScore(79.9);ptr_stu1-AddScore(800);ptr_stu1-showInfo();Student stu2(刘强东,不爱美人);stu2.AddScore(78.9);stu2.AddScore(77.9);stu2.AddScore(72.9);stu2.AddScore(79.9);stu2.AddScore(90);stu2.showInfo();// Student scholar1 stu2.GetSuperScholars(*ptr_stu1); 返回new出来的结果接受的确实栈类型矛盾空间释放的时候会出问题
// Student scholar2 ptr_stu1-GetSuperScholars(stu2);Student scholar1 stu2.GetSuperScholars(*ptr_stu1); //返回迪丽热巴的引用Student scholar2 ptr_stu1-GetSuperScholars(stu2); //返回迪丽热巴的引用cout 学霸是 scholar1.GetName() \t scholar2.GetName() endl;delete ptr_stu1;return 0;//scholar1 栈内存中定义的main 结束后会自动释放//scholar2 栈内存中定义的main 结束后会自动释放
}//Student.h
//
// Created by 陈莹莹 on 2021/2/2.
//
#ifndef HELLOWORLD_STUDENT_H
#define HELLOWORLD_STUDENT_H
#include iostream
using namespace std;class Student {
private:string m_name;string m_desc;int m_age;float *scores; // 学生的分数数组在某次构造的时候进行初始化int scoreCount; // 学生成绩的个数public:Student(); // 默认构造函数Student(string, string);~Student();void showInfo();void InitScores(); // 初始化学生的成绩数组默认分配一个元素空间void AddScore(float score);// 返回值是引用时是非常危险的。this可以直接修改属性建议在函数声明处加一个const,限制在该函数内部不能通过this指针修改属性,太麻烦了要该的地方太多了Student GetSuperScholars(Student ); //返回学霸对象// 参数处加const 保证不对该参数进行修改。float GetTotal();string GetName() {return m_name;}};#endif //HELLOWORLD_STUDENT_H
//Student.cpp
//
// Created by 陈莹莹 on 2021/2/2.
//
#include iostream
#include Student.h
using namespace std;Student::Student() {cout 默认构造函数 endl;InitScores();
}
//Student::Student(string name, string desc){
// // 参数列表不同函数重载
// m_name name;
// m_desc desc;
// cout 设置带参构造 endl;
//}
//初始化参数列表的写法和上上面的带参数构造方法完全相同
Student::Student(string name, string desc):m_name(name), m_desc(desc){cout 设置带参构造 endl;InitScores();
};void Student::showInfo() {cout m_desc m_name endl;for(int i 0; i scoreCount-1; i){cout this-scores[i] \t;}cout endl;
}
// C的类方法写(我这么写编译不过)
//void MyShow(const Student* this){
// this-
//}
void Student::InitScores() {this - scores new float [1];this - scoreCount 1;
}
void Student::AddScore(float score) {this - scores[this-scoreCount -1] score;// 1. 创建一个新数组分配scoreCount1 个空间// 2. 复制愿数组中的内容到新数组中// 3. scoreCount// 4. scores指向新数组float *newScores new float [scoreCount 1];float *oldScores scores;memcpy(newScores, scores, sizeof(float) * scoreCount);scoreCount;scores newScores;delete oldScores; // 删除原来的指针
}
Student Student::GetSuperScholars(Student otherstu){// otherstu 要对比的另一学生对象// 返回总分比较大的那个学生对象// 分别计算两个学生的总分if(this-GetTotal() otherstu.GetTotal()){return *this;}else{return otherstu;}}
float Student::GetTotal(){float sum 0;for(int i 0; i scoreCount; i){sum scores[i];}return sum;}
Student::~Student() {cout m_name 被释放 endl;delete this - scores;}输出
设置带参构造
微胖女孩迪丽热巴
78.9 77.9 72.9 79.9 800
设置带参构造
不爱美人刘强东
78.9 77.9 72.9 79.9 90
学霸是迪丽热巴 迪丽热巴
迪丽热巴被释放
刘强东被释放小结类就是自己定义的数据类型对象就是变量