C++封装

封装:把一些信息封装起来,只暴露出用户关心的信息。通过访问限定符将想要暴露的暴露出来,想要隐藏的隐藏起来。

类和对象

对象实例化:

从栈中实例化:类的名字,对象名字

从堆中实例化:用new申请一块内存,用指针指向这快内存的地址。申请使用完之后要将内存释放置空掉(delete 指针 、指针=NULL)。

从栈上和从对上访问成员函数的方法:(左图上从栈上,右图是从堆上)

image-20201107160143577

栈:

#include<iostream>` `using namespace std;` `class Coordinate{` `public:` `int x;` `int y;` `void printX(){` `cout<<x<<endl;` `}` `void printY(){` `cout<<y<<endl;` `}` `};` `int main(){` `Coordinate c;` `c.x=10;` `c.y=20;` `c.printX();` `c.printY();` `return 0;` `}

堆:

#include<iostream>
using namespace std;
class Coordinate{
public:
int x;
int y;
void printX(){
cout<<x<<endl;
}
void printY(){
cout<<y<<endl;
}
};
int main(){
``Coordinate *p=new Coordinate(); if(NULL==p){ return 0; } p->x=100; p->y=200; p->printX(); p->printY(); delete p; p=NULL; return 0; }`

数据成员和成员函数

数据成员也叫属性。

成员函数也叫方法。

字符串类型

与java不同的是使用字符串需要引入string头文件;string首字母小写;字符串用双引号,字符使用单引号。

1、string s1;此时s1是一个空串。

2、string s2="wangjiaer";赋初始值。

3、string s3(s2);s3是s2的一个副本。

4、strings4(n,'c');s4初始化为字符‘c’的n个副本。

5、string的常用操作:

image-20201107162917568

非法写法:string s=“hello”+“world”;

练习:

#include <iostream>
#include <string>
using namespace std;
/**
  * 定义类:Student
  * 数据成员:名字、年龄
    */
    class Student
    {
    public:
    // 定义数据成员名字 m_strName 和年龄 m_iAge
    string m_strName;
    int m_iAge;
    };
int main()
{
    // 实例化一个Student对象stu
    Student stu;
    // 设置对象的数据成员
    stu.m_strName = "慕课网";
    stu.m_iAge = 2;  
// 通过cout打印stu对象的数据成员
cout << m_strName << " " << m_iAge<< endl;
return 0;
}

报错:倒数第三行m_strName 和 m_iAge没有定义。

原因:在main函数中我声明定义的``stu.m_strName = "慕课网"; stu.m_iAge = 2; 这个是对象stu的定义。而m_strName 和 m_iAge是没有定义的。按照要求 // 通过cout打印stu对象的数据成员
,正确写法是:

cout << stu.m_strName << " " << stu.m_iAge<< endl;

属性封装

面向对象的基本思想:对象在程序中的所有操作都转换成对函数的操作。

get/set方法:对私有属性进行操作。

练习:

include

include

using namespace std;

/**

  • 定义类:Student
  • 数据成员:m_strName
  • 数据成员的封装函数:setName()、getName()
    */
    class Student
    {
    public:
    // 定义数据成员封装函数setName()
    void setName(string _name){
    m_strName=_name;

    }
    // 定义数据成员封装函数getName()
    string getName(){
    return m_strName;
    }
    //定义Student类私有数据成员m_strName
    private:
    string m_strName;
    };

int main()
{
// 使用new关键字,实例化对象
Student *str = new Student();
// 设置对象的数据成员
str->setName("慕课网");
// 使用cout打印对象str的数据成员
cout<getName();
// 将对象str的内存释放,并将其置空
delete str;
str=NULL;
return 0;
}

类内定义和内联函数

内联函数:inline;

类内定义:函数和函数体写在类的内部。

类内定义的成员函数,编译器会优先将其编译为inline函数。

类外定义:同文件类外定义,分文件类外定义

同文件类外定义:成员函数的定义在类外定义,但是需要声明是哪个类的方法。

分文件类外定义:将成员函数的声明写在一个与类名相同的头文件中(类名.h),在类中需要将该头文件引入,才可以定义头文件中的方法。

构造函数和析构函数

image-20201109165357020

对象初始化:有且仅有一次或者根据条件初始化。

构造函数:初始化。在对象实例化时被自动调用。构造函数在初始化实例对象被调用且仅被调用一次。构造函数与类同名,没有返回值,可有多个重载形式,但是在实例化对象时候,只会用到一个构造函数。用户如果没有定义构造函数,编译器会自动生成一个构造函数。

在实例化对象时不需要传递参数的叫做默认构造函数。无参构造函数默认构造函数。

拷贝构造函数:

image-20201109200222345

拷贝构造函数的参数是确定的,不能重载

析构函数

格式:~类名()

析构函数没有参数,不能被重载.

image-20201109200845924

image-20201109200951058

练习:

#include <iostream>
#include <string>
using namespace std;
/**
 * 定义类:Student
 * 数据成员:m_strName
 * 无参构造函数:Student()
 * 有参构造函数:Student(string _name)
 * 拷贝构造函数:Student(const Student& stu)
 * 析构函数:~Student()
 * 数据成员函数:setName(string _name)、getName()
   */
class Student{
    public:
        Student(){
        };
        Student(string _name){
             _name=m_strName;
        }
        Student(const Student& stu){
    }
    ~Student(){
        
    }
    void setName(string  _name){
       m_strName=_name;
    }
    string getName(){
        return m_strName;
    }
private:
    string m_strName;
    }; 
int main(void)
{
    // 通过new方式实例化对象*stu
    Student *stu = new Student();
    // 更改对象的数据成员为“慕课网”
	stu->setName("慕课网");
    // 打印对象的数据成员
	cout<<stu->getName();
	return 0;
}

上面之前一直没有输出的原因是,我定义Set方法的时候,写成了:

void setName(string name){
name=m_strName
;
}

这是典型的错误.

正确应该是上面完整代码那样:

void setName(string _name){
m_strName=_name; //赋值语句
}

对象指针

对象指针:用一个指针指向对象。p->x=10;(*p).x=10;

类名 *p2=&p1;用p2的指针指向p1的地址。

this指针

如果参数和数据成员同名,编译器无法分辨。

this指针就是指向对象自身数据的指针。所以如果重名,用this指针就行。

image-20201113172723093

每次调用成员函数都使用到了this指针。

this指针就是指向所在对象的地址。

对象数组

堆中实例化的数组需要手动销毁释放内存,在栈中实例化的数组,系统自动回收内存。

对象成员

存在的问题:

===================Coordinate.h

class Coordinate{
public:
Coordinate();
~Coordinate();
void setX(int x);
int getX();
void setY(int y);
int getY();
private:
int m_iX;
int m_iY;
};

======================Line.h

include "Coordinate.h"

class Line{
public:
Line();
~Line();
void setA(int x,int y);
void setB(int x,int y);
void printInfo();
private:
Coordinate m_coorA;
Coordinate m_coorB;
};

=====================Coordinate.cpp

include"Coordinate.h"

include

Coordinate::Coordinate(){
cout<<"coor()";
}
Coordinate::~Coordinate(){
cout<<"~coor()";
}
void Coordinate::setX(int x){
m_iX=x;
}
int Coordinate::getX(){
return m_iX;
}
void Coordinate::setY(int y){
m_iY=y;
}
int Coordinate::getY(){
return y;
}

========================Line.cpp

include"Line.h"

include

Line::Line(){
cout<<"line()";
}
Line::~Line(){
cout<<"~line()";
}
void Line::setA(int x,int y){
m_coorA.setX(x);
m_coorA.setY(y);
}
void Line::setB(int x,int y){
m_coorB.setX(x);
m_coorB.setY(y);
}
void Line::printInfo(){
cout<<m_coorA.getX()<<m_coorA.getY();
cout<<m_coorB.getX()<<m_coorB.getY();

}

===========================demo.cpp

include

include "Line.h"

using namespace std;
int main(){
Line *p=new Line();
delete p;
p=NULL;
return 0;
}

报错:

image-20201111213830337

拷贝构造函数

浅拷贝:将值直接拷贝过去。这样会导致两个值指向的地址是用一个地址,导致释放内存的时候会出现释放两次,一定出问题。

深拷贝:为了解决上面的问题。在拷贝构造函数中,给要复制的对象申请内存。将传入的对象对应位置的内存拷贝到申请的内存中。

对象成员指针

一个指针在32为编译器下指向4个内存单元。

对象需要看他包含几个指针。

const

常对象成员和常成员函数

image-20201113210456449

由const修饰的对象成员和成员函数。

image-20201113211130033

互为重载:常成员函数和成员函数。调用名字相同的常成员函数时候需要在前面加上const。

image-20201113211332588

常指针与常引用

image-20201113213037529

image-20201113213055630

没有const修饰的时候要求读写权限。

迷宫--算法

左手规则,右手规则。

(出口和入口不是同一个口)

迷宫类:二维 数组 0-墙/1-路

人类:人;人的朝向;当前位置;人前一个位置;人的速度;

​ 构造函数,封装函数,朝不同方向前进的函数;开始函数;转弯函数。

image-20201113215601724