c++面向对象的三大特性:封装、继承、多态

4.1封装

4.1.1封装的意义

封装的意义

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

封装意义一:

在设计类的时候,属性和行为写在一起,表现事物

语法:class  类名{  访问权限 :属性 / 行为  };

代码

#include<iostream>
using namespace std;
//圆周率
const double PI = 3.14;class Circle
{//访问权限
public://属性//定义圆的半径int m_r;//行为//获取圆的周长double calculateZC() {return 2 * PI * m_r;}
};int main()
{//通过圆类,创建圆的对象//c1就是一个具体的圆Circle c1;c1.m_r = 10;//创建圆的半径,进行赋值操作cout << "圆的周长为:" << c1.calculateZC()<< endl;system("pause");return 0;
}

案例2:设计学生类

#include<iostream>
using namespace std;
#include<string>
//设计一个学生类,属性由姓名和学号
//可以给姓名和学号赋值,可以显示学生的姓名和学号
class Student
{
public://属性string m_Name;int m_ID;//行为void setName(string name){m_Name = name;}void setID(int id){m_ID = id;}void showStudent(){cout << "姓名:" << m_Name << " 学号:" << m_ID << endl;}
};int  main()
{Student s;s.m_Name=("张三");s.m_ID=(1);s.showStudent();system("pause");return 0;
}

总结:

1.类中的属性和行为,统称为成员

2.属性也称为成员属性、成员变量

3.行为也称为成员函数、成员方法

封装意义二:

类在设计时,可以把属性和行为放在不同的权限下,加以控制

访问权限有三种

  • public  公共权限
  • protected  维护权限
  • private   私有权限

代码

#include<iostream>
using namespace std;
//访问权限
//公共权限 public     类内可以访问  类外可以访问
//保护权限 protected  类内可以访问  类外不可以访问
//私有权限 private    类内可以访问  类外不可以访问
class Person
{
public:string m_Name;
protected:string m_Car;
private:int m_Password;public:void func(){m_Name = "张三";m_Car = "拖拉机";m_Password = 123456;}
};int main()
{Person p1;p1.m_Name = "李四";  //公共访问 类外可以访问//p1.m_Car = "奔驰";   保护访问 类外不可以访问//p1.m_Password=12542; 私有访问 类外不可以访问system("pause");return 0;
}

总结:

1.保护权限和私有权限的区别:在类内对函数进行调用时,保护权限可以在类外调用,但私有权限不可以

4.1.2 struct 和class区别

区别:默认的访问权限不同

  • struct默认权限为公共
  • class默认权限为私有

代码

#include<iostream>
using namespace std;
class C1
{int m_A;
};struct C2
{int m_A;
};
int main()
{C1 c1;//c1.m_A = 100;  class默认访问权限为私有,只能类内访问C2 c2;c2.m_A = 100;  //struct默认访问权限为公共,类内类外都能访问system("pause");return 0;
}

4.1.3成员属性设置为私有

优点

  • 将所有成员属性设置为私有,可以自己控制读写权限
  • 对于写权限,我们可以检测数据的有效性

代码

#include<iostream>
using namespace std;
#include<string>
class Person
{
public://姓名void setName(string name) {m_Name = name;}string getName() {return m_Name;}//年龄void setAge(int age) {if (age < 0 || age>150) {m_Age = 0;cout << "输入有误" << endl;return;}m_Age = age;}int getAge() {return m_Age;}//情人void setLover(string lover) {m_Lover = lover;}private:string m_Name;  //姓名 可读可写int m_Age;      //年龄 只读string m_Lover; //情人 只写
};int main()
{Person p;p.setName("张三");cout << "姓名:" << p.getName() << endl;p.setAge(175);cout << "年龄:" << p.getAge() << endl;p.setLover("苍劲");//cout << "情人:" << p.m_Lover << endl;  只写属性,无法访问system("pause");return 0;
}

案例一:立方体类的设计

要求:求出立方体的体积和面积,分别利用全局函数和成员函数判断两个立方体是否相等

代码

#include<iostream>
using namespace std;
//立方体类的设计
//1、创建立方体类
//2、设计属性
//3、设计行为 获取立方体面积、体积
//4、分别利用全局函数和成员函数 判断两个立方体是否相等class Cube
{
public://设置长宽高,获取长宽高void setL(int l) {m_L = l;}int getL() {return m_L;}void setW(int w){m_W = w;}int getW(){return m_W;}void setH(int h){m_H = h;}int getH(){return m_H;}//获取面积、体积int caculateS(){return 2 * m_L*m_H + 2 * m_H*m_W + 2 * m_L*m_W;}int caculateV() {return m_L * m_H*m_W;}//利用成员函数来判断两个立方体是否相等bool isSameByClass(Cube &c){if (m_H == c.getH() && m_W == c.getL() && m_W == c.getW()){return true;}return false;}private:int m_L;int m_W;int m_H;
};
//利用全局函数来判断两个立方体是否相等
bool isSame(Cube &c1, Cube &c2)
{if (c1.getH() == c2.getH() &&c1.getL() == c2.getL() && c1.getW() == c2.getW()) {return true;}return false;
}
int main()
{Cube c1;c1.setH(10);c1.setL(10);c1.setW(10);cout << "立方体的面积:" << c1.caculateS() << endl;cout << "立方体的体积:" << c1.caculateV() << endl;//创建第二个立方体类Cube c2;c2.setH(10);c2.setL(10);c2.setW(10);//利用全局函数判断bool ret = isSame(c1, c2);if (ret) {cout << "c1和c2是相等的" << endl;}else{cout << "c1和c2是不相等的" << endl;}//利用成员函数判断ret = c1.isSameByClass(c2);if (ret){cout << "成员函数c1和c2是相等的" << endl;}else{cout << "成员函数c1和c2是不相等的" << endl;}system("pause");return 0;
}

案例二:点和圆的关系

设计圆的半径和圆心的位置,并判断某个点相对于圆的位置

代码

#include<iostream>
using namespace std;class Point
{
public:void setX(int x) {m_X = x;}int getX() {return m_X;}void setY(int y){m_Y = y;}int getY(){return m_Y;}
private:int m_X;int m_Y;};class Circle
{
public://设置、获取半径void setR(int r) {m_R = r;}int getR() {return m_R;}//设置、获取圆心void setCenter(Point center) {m_Center = center;}Point getCenter() {return m_Center;}private:int m_R;//在类中可以让另一个类 作为本类中的成员Point m_Center;
};//判断点和圆的关系
void isInCircle(Circle &c,Point &p)
{int distance =(c.getCenter().getX() - p.getX())*(c.getCenter().getX() - p.getX()) +(c.getCenter().getY() - p.getY())*(c.getCenter().getY() - p.getY());int rDistance = c.getR()*c.getR();if (distance == rDistance) {cout << "点在圆上" << endl;}else if (distance > rDistance) {cout << "点在圆外" << endl;}else {cout << "点在圆内" << endl;}}int main()
{//创建圆Circle c;c.setR(10);Point center;center.setX(10);center.setY(0);c.setCenter(center);//创建点Point p;p.setX(10);p.setY(11);//判断关系isInCircle(c, p);system("pause");return 0;
}

总结:

1.类中可以让另一个类,作为本类中的成员

2.当类较多时,可以拆分成多个头文件

4.2对象的初始化和清理

4.2.1构造函数和析构函数

c++利用构造函数和析构函数来解决对象的初始化和清理,如果我们不提供构造和析构,编译器会提供。但其提供的构造函数和析构函数是空实现

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

构造函数语法:类名(){}

1.构造函数,没有返回值也不写void

2.函数名称与类名相同

3.构造函数可以有参数,因此可以发生重载

4.程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数:~类名(){}

1.析构函数,没有返回值也不写void

2.函数名称与类名相同,在名称前加上符号~

3.析构函数不可以有参数,因此不可以发生重载

4.程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

代码

#include<iostream>
using namespace std;
class Person
{
public:Person() {cout << "构造函数的调用" << endl;}~Person(){cout << "析构函数的调用" << endl;}
};void test01()
{Person p;  //局部变量——在栈上的数据,test01执行完毕后,释放这个对象
}int main()
{//test01();Person p;system("pause");return 0;
}

4.2.2构造函数的分类及调用

分类

  • 按参数分

    • 有参构造
    • 无参构造
  • 按类型分
    • 普通构造
    • 拷贝构造

调用方式

  • 括号式
  • 显示式
  • 隐式转换法

代码

using namespace std;
//1.构造函数的分类
//按参数分,按类型分
class Person
{
public://无参(默认)构造函数Person() {cout << "无参构造函数" << endl;}//有参构造函数Person(int a) {age = a;cout << "有参构造函数" << endl;}//拷贝构造函数Person(const Person &p) {age = p.age;cout << "拷贝构造函数" << endl;}//析构函数~Person() {cout << "析构函数" << endl;}
public:int age;
};
//2.构造函数的调用
//调用无参构造函数
void test01()
{Person p; //调用无参(默认)构造函数
}
//调用有参构造函数
void test02()
{//2.1括号法Person p1(10);//注意事项1:调用无参构造函数不能加括号,如果加了编译器会认为是一个函数声明//Person p2();//2.2显示法Person p2 = Person(10);Person p3 = Person(p2);//Person(10)单独写就是匿名对象,当前行执行结束后,系统会立即回收掉匿名对象//注意事项2:不能利用拷贝构造函数 初始化匿名对象 编译器会认为是对象声明//Person (p2) ,Person p3 =Person (p2)//2.3隐式转换法Person p4 = 10; //Person p4 =Person(10)Person p5 = p4; //Person p5 =Person(p4)
}int main()
{test02();system("pause");return 0;
}

4.2.3 拷贝构造函数调用时机

三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象

代码

#include<iostream>
using namespace std;class Person
{
public:Person() {cout << "Person默认构造函数" << endl;}Person(int age) {cout << "Person有参构造函数" << endl;m_Age = age;}Person(const Person &p) {m_Age = p.m_Age;cout << "Person拷贝构造函数" << endl;}~Person(){cout << "Person析构函数" << endl;}int m_Age;
};
//1、使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{Person p1(20);Person p2(p1);cout << "P2的年龄为:" << p2.m_Age << endl;
}//2、值传递的方式给函数参数传值
void doWork(Person a)
{}
void test02()
{Person p;doWork(p);
}//3、值方式返回局部对象
Person  doWork2()
{Person p1;cout << (int*)&p1 << endl;return p1;
}
void test03()
{Person p = doWork2();cout << (int*)&p << endl;
}int main()
{//test01();//test02();test03();system("pause");return 0;
}

4.2.4 构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则:

  • 如果用户定义有参构造函数,c++不在提供默认(无参)构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数

代码

#include<iostream>
using namespace std;class Person
{
public:Person(){cout << "Person默认构造函数" << endl;}Person(int age){cout << "Person有参构造函数" << endl;m_Age = age;}Person(const Person &p){m_Age = p.m_Age;cout << "Person拷贝构造函数" << endl;}~Person(){cout << "Person析构函数" << endl;}int m_Age;
};
//默认拷贝构造函数,对属性进行只拷贝
void test01()
{Person p;p.m_Age = 18;Person p2(p);cout << "p2的年龄为;" << p2.m_Age << endl;
}void test02()
{Person p(28);Person p2(p);cout << "p2的年龄为;" << p2.m_Age << endl;
}
int main()
{//test01();test02();system("pause");return 0;
}

4.2.5 深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

  • 浅拷贝带来的问题:堆区的内存重复释放

深拷贝:在堆区重新申请空间,进行拷贝操作

代码

#include<iostream>
using namespace std;class Person
{
public:Person() {cout << "Person的默认构造函数调用" << endl;}Person(int age,int height) {cout << "Person的有参构造函数调用" << endl;m_Age = age;m_Height = new int(height);//堆区数据由程序员开辟并释放}Person(const Person & p) {cout << "Person的拷贝构造函数调用" << endl;m_Age = p.m_Age;//m_Height=p.m_Height 是编译器默认实现的代码m_Height = new int(*p.m_Height);}~Person(){//析构代码——将堆区开辟数据释放cout << "Person的析构函数调用" << endl;if (m_Height != NULL) {delete m_Height;m_Height = NULL;//防止野指针出现,进行置空操作}}public:int m_Age;int *m_Height;
};void test01()
{Person p1(18, 160);cout << "p1的年龄:" << p1.m_Age << "身高:" << *p1.m_Height << endl;Person p2(p1);cout << "p2的年龄:" << p2.m_Age << "身高:" << *p2.m_Height << endl;
}
int main()
{test01();system("pause");return 0;
}

4.2.6 初始化列表

作用:c++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)...{}

代码

#include<iostream>
using namespace std;class Person
{
public:Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}
public:int m_A;int m_B;int m_C;
};void test01()
{Person p(10, 20, 30);cout << "m_A= " << p.m_A << endl;cout << "m_B= " << p.m_B << endl;cout << "m_C= " << p.m_C << endl;
}
int main()
{test01();system("pause");return 0;
}

4.2.7 类对象作为类成员

c++类中的成员可以是另一个类的对象,我们称该成员为 对象成员

代码

#include<iostream>
using namespace std;
#include<string>
//手机类
class Phone
{
public:Phone(string pName){cout << "Phone的构造函数调用" << endl;m_PName = pName;}~Phone(){cout << "Phone的析构函数调用" << endl;}string m_PName;
};
//人类
class Person
{
public:Person(string name, string pName) :m_Name(name), m_Phone(pName){cout << "Person的构造函数调用" << endl;}~Person(){cout << "Person的析构函数调用" << endl;}string m_Name;Phone m_Phone;
};
//当其他类对象作为本类成员,构造时先构造类对象,再构造自身;析构的顺序与构造相反
void test01()
{Person p("张三", "苹果11");cout << p.m_Name << "拿着" << p.m_Phone.m_PName << endl;
}int main()
{test01();system("pause");return 0;
}

4.2.8 静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为

  • 静态成员变量

    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量

代码

#include<iostream>
using namespace std;class Person
{
public:static void func() {m_A = 100; //静态成员函数 可以访问 静态成员变量//m_B = 200; 静态成员函数 不可以访问 非静态成员变量cout << "static void func调用" << endl;}static int m_A;int m_B;//静态函数也是有访问权限的——无法访问私有权限
private:static void func2(){cout << "static void func2调用" << endl;}
};
int Person::m_A = 0;//有两种访问方式
void test01()
{//1、通过对象访问Person p;p.func();//2、通过类名访问Person::func();//Person::func2(); 私有权限访问不到
}
int main()
{test01();system("pause");return 0;
}

4.3C++对象模型和this指针

4.3.1 成员变量和成员函数分开存储

在c++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上

代码

#include<iostream>
using namespace std;
//成员变量 和 成员函数 是分开存储的
class Person
{int m_A;               //非静态成员变量  属于类的对象上static int m_B;        //静态成员变量    不属于类对象上void func() {}         //非静态成员函数  不属于类对象上static void func2() {} //静态成员函数    不属于类对象上
};
void test01()
{Person p;//空对象占用内存空间为:1//C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置//每个空对象也应该有一个独一无二的内存地址cout << "size of p=" << sizeof(p) << endl;
}void test02()
{Person p;cout << "size of p=" << sizeof(p) << endl;
}
int main()
{//test01();test02();system("pause");return 0;
}

4.3.2 this指针概念

this指针指向被调用的成员函数所属的对象

this指针不需要定义,直接使用即可

this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this

代码

#include<iostream>
using namespace std;
class Person
{
public:Person(int age) {//this指针指向 被调用的成员函数 所属的对象this->age = age;}Person& PersonAddPerson(Person &p) {this->age += p.age;//this指向p2的指针,而*this指向的就是p2这个对象本体return *this;}int age;
};
//1、解决名称冲突
void test01()
{Person p1(10);cout << "年龄为:" << p1.age << endl;
}
//2、返回对象本身用*this
void test02()
{Person p1(10);Person p2(10);p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);cout << "p2年龄为:" << p2.age << endl;}
int main()
{test01();test02();system("pause");return 0;
}

4.3.3空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

如果用到this指针,需要加以判断保证代码的健壮性

代码

#include<iostream>
using namespace std;
class Person
{
public:void ShowPersonName() {cout << "this is Person Name" << endl;}void ShowPersonage() {if (this == NULL) {return;}cout << "年龄为:" << this->m_age << endl; //报错原因是因为传入的指针为空指针}int m_age;
};
void test01()
{Person *p = NULL;p->ShowPersonage();p->ShowPersonName();}
int main()
{test01();system("pause");return 0;
}

4.3.4const修饰成员函数

常函数

  • 成员函数后加const,称其为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象

  • 声明对象前加const,称其为常对象
  • 常对象只能调用常函数

代码

#include<iostream>
using namespace std;
class Person
{
public://this指针的本质 是指针常量 指针的指向是不可以修改的//const Person * const this;//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改void showPerson() const {//this->m_A = 100;//this = NULL; //this指针是不可以修改指针的指向this->m_B = 100;}void func() {}int m_A;mutable int m_B;//特殊变量,在常函数中也可以修改这个值
};void test01()
{Person p;p.showPerson();
}
//常对象void test02()
{const Person p;//在对象前加const,变为常对象//p.m_A = 100;p.m_B = 100;//常对象只能调用常函数p.showPerson();//p.func();  常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性
}
int main()
{system("pause");return 0;
}

第四章——类和对象(一)相关推荐

  1. 第四章类和对象 习题答案

    一.选择题1.能提供封装的C++的下列关键字是(C)A.whileB. unionC. classD. for2.在下面所列项中,不是面向对象的特点的是(C)A. 多面性B. 抽象性和封装性C. 多线 ...

  2. C++语言程序设计——知识点复盘(第四章 类与对象)

    目录 面向对象程序设计的基本特点 1.抽象 2.封装 3.继承 4.多态 类和对象 类的成员函数 1.成员函数的实现 2.目的对象. 3.带默认形参值的成员函数 4.内联成员函数 构造函数 析构函数 ...

  3. c++ 复习 第四章类与对象

    小知识点 成员函数参数的默认值要写在类中 类外就不要再写默认值了 c++鼓励数据和操作封装在一起 对象之间的相互通信是通过( 调用成员函数 )实现的. 在类中,如果不作特别说明,所有的数据均为私有类型 ...

  4. Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25

    Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25 目录 Rule20 接口优于抽象类 Rule21 为后代设计接口 Rule22 接口只用于定义类型 ...

  5. C++编程入门系列之十四(类与对象:构造函数和析构函数)

    C++编程入门系列之十四(类与对象:构造函数和析构函数) 鸡啄米上一节中给大家讲解了类的声明.成员的访问控制和对象,今天鸡啄米给大家讲C++编程入门时同样必须掌握的构造函数和析构函数.从上一讲开始已经 ...

  6. Kotlin学习笔记 第二章 类与对象 第三节接口 第四节 函数式接口

    参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...

  7. 第三章类与对象基础 ① 笔记

    1. 内容回顾 1.1. 课前测试 1.2. 上节内容 2. 本章重点 2.1. 类和对象的概念 2.2. 类的基本组成 2.3. 构造方法 2.4. 成员方法 3. 具体内容 3.1. 类和对象的概 ...

  8. java怎么给类中的私有变量赋值_Java核心技术笔记分享------第二章 类与对象

    对象与类 一.面向对象思想的概述 1>面向对象与面向过程: 二者都是一种思想,面向对象是相对于面向过程而言的.面向过程强调的是功能行为.面向对象,将功能封装进对象,强调具备了功能的对象. 面向对 ...

  9. Python程序开发——第六章 类与对象

    目录 一.类 (一)类.对象 (二)类的定义 (三)对象的创建和使用 二.限制对象访问 (一)定义私有成员.方法 (二)访问私有成员.方法 三.构造方法和析构方法 (一)构造方法 (二)析构方法 四. ...

最新文章

  1. python处理csv中的缺失值_Python中重复值、缺失值、空格值处理
  2. 基于TextRank的关键词提取算法
  3. 汇编——NT中读取MBR内容
  4. Linux安装或升级openssh步骤和可能遇到的问题
  5. 2.12 矩阵及乘法重要总结
  6. 基于springcloud的开发者实践:hystrix-dashboard熔断仪表盘
  7. 傻瓜学python_傻瓜式学Python3——列表
  8. 求n!的算法和C 实现
  9. Raki的读paper小记:An Effective Transition-based Model for Discontinuous NER
  10. 移动端开发vw+rem布局,即等比缩放布局(什么是vw?如何设置根元素html的字体大小?如何换算vw单位?文末:移动端开发步骤详解链接)
  11. iphone 操作手势种类
  12. 人工智能是否会改写商业规则
  13. 【知识】SpringBoot项目结构目录
  14. 《武魂》物理引擎特效全解析
  15. Vue之watch监听
  16. [实用理论] 互联网广告的产业链:广告主(advertiser),媒体(publisher),广告商(agency)
  17. Presto 在字节跳动的应用
  18. doceker使用教程(一)
  19. 数据集成平台的特点(Oracle service bus)
  20. 6个常见的开源人脸数据库及其数据特征

热门文章

  1. php file_get_contents referer,php怎样设置捏造referer地点_后端开发
  2. 月薪10000+,每天五点半准时下班
  3. C语言写边界,C语言基本类型边界值
  4. 山东省论文期刊代发代写机构
  5. 基于SpringBoot的家政服务管理平台
  6. VirtureBox虚拟机另类复用方法
  7. Zookeeper 4 Zookeeper JavaAPI 操作 4.9 模拟12306 售票案例
  8. 国行Switch卡带报错2016-2101的问题
  9. 软件设计原则之接口隔离原则、合成复用原则、迪米特原则
  10. android手机卡怎么办,安卓手机卡慢怎么办 安卓手机卡慢解决方案【详解】