面向对象编程思想(✔)

00 引入

计算机主要是用来解决现实世界中的问题的
面向过程(POP)和面向对象(OOP)是两种解决问题的思想,这些思想指导我们如何去分析、设计和开发软件

c和c++的区别是什么?

        最重要的就是c++的编程思想是面向对象(Object Oriented Programming),而c的编程思想是面向过程(Process-oriented programming),这是它们的本质区别,如果你在使用c++编程时使用的还是面向过程的编程思想,那么还不如使用c,因为这样的做法已经丢掉了c++的思想精髓。在学习一门语言时,最重要的是学习它的设计思想,因为语法都是大同小异的,很快便可以掌握


1. “自顶向下,逐步求精”的面向过程程序设计

        面向过程程序设计思想的一种解决思路 — 提出问题,分析问题的处理流程,将大问题分解为小问题,如果小问题比较复杂,就继续划分小问题为更小的问题,然后通过小模块一 一解决小问题,最后再根据整个业务流程将这些小问题串在一起(调用函数),这样就达到了解决所有问题的目的,如下:


        于是,这样从问题出发,自顶向下、逐步求精的开发思想我们称之为“面向过程程序设计思想”,因为它主要是解决问题中的一个个过程。这种思想也是最早的编程语言的思想,因为它比较符合我们解决问题的方法,其他语言比如早期的Fortran、JavaScript等都是如此

它的优点如下:

       (1) 优点1:程序结构简单 — 仅由三种基本结构组成

          面向过程编程的思想在程序实现上只需要三种控制结构,即顺序、选择、循环。通过这三种基本结构,我们就可以解决任何问题,所以我们可以专注于程序逻辑的实现,不需要学习、记忆更多的控制结构

        (2) 优点2:分而治之,各个击破

          当我们在解决复杂问题时,通常采用的就是“分而治之”的策略,即把大问题分解为小问题,然后再各个击破这些小问题,这样整个大问题就得到了解决。所以,面向过程程序设计思想也是采用这种“分而治之”的策略,把较大的程序按照业务逻辑划分为多个子模块,然后再分工逐个完成这些子模块。最后按照业务流程把他们组织起来,最终使得整个问题得到解决。把大问题细分为小的问题然后“各个击破”,符合人们思考问题的一般规律,更易于理解,也易于人们掌握,通过分解问题,降低了问题的复杂度,使得程序易于实现和维护,另外,部分分解后的小问题(子模块)可以重复使用,从而避免了重复开发,而多个子模块也可以由多人分工协作完成,提高开发效率

        (3) 优点3:自顶向下,逐步求精

          面向过程程序设计思想倡导的方法是“自顶向下,逐步求精”,即从宏观角度考虑,按照功能或者业务逻辑划分程序的子模块,定义程序的整体结构,然后再对各个子模块逐步细化,最终分解到程序语句为止。这种方法可以使程序员全面考虑问题,使程序的逻辑关系清晰明了。它让整个开发过程从原来的考虑 “怎么做” 变成考虑 “先做什么,再做什么”,流程就更加清晰了

        缺点:

                但是,一件事物必定有优点也有缺点,面向过程的程序设计当然也躲不过这种规律,并且缺点的凸显往往是在程序的复杂性越来越高时才会出现的,那么这种设计思想的缺点是什么呢?

          在面向过程程序设计时,数据和操作往往是分离的,这就导致如果数据的结构发生了变化,那么操作数据的函数不得不重新改写,这个代价是非常高的。并且,数据往往不具有封装性,很多变量都会暴露在全局,加大了被任意修改的风险。另外,一旦涉及到模块的重新划分,往往需要修改原来写好的功能模块。且这种数据和操作相互分离的特点使得一些模块跟具体的应用环境结合紧密,旧有的程序模块就很难在新的程序中得到复用。所以,这些固有的缺点使它越来越难以适应大型的软件项目的开发。正是在这样的历史背景下,一些新的程序设计思想开始不断涌现,而面向对象的程序设计思想就是最重要的一环

2. 万般皆对象:面向对象程序设计

        面向对象的程序设计(OOP)是面向过程程序设计的继承和发展,它不仅汲取了后者的精华,而且以一种更加接近人类思维的方式来分析和解决问题:程序是对现实世界的抽象和描述,现实世界的基本单元是物体,与之对应的,程序中的基本单元是对象

        面向对象思想认为:现实世界是由对象组成的,无论大到一个国家还是小到一个原子,都是如此。并且对象都由两部分组成 — 描述对象状态或属性的数据(变量)以及描述对象行为或者功能的方法(函数)。并且与面向过程不同,面向对象是将数据和操作数据的函数紧密结合,共同构成对象来更加精确地描述现实世界,这是面向过程和面向对象两者最本质的区别

  之前提到面向过程的缺点,即面向过程中数据和操作是分离的,当问题规模比较小时,需求变化不大的时候,面向过程工作都做的很好。 但是,当问题的规模越来越大、越来越复杂时,面向过程就显得力不从心了,即修改了某个 结构体,就不得不修改与之相关的所有过程函数,而一个过程函数的修改,往往又会设计到其他数据结构,在规模比较小的时候容易解决,但是当系统规模越来越大时,特别是涉及到了多人协作开发,这就非常困难,这就是著名的软件危机。 正是如此,面向对象的程序设计应运而生,它的主要特点是封装、继承和多态。封装即将数据和操作封装在一起,并避免了局部变量的暴露,而只提供接口;继承可以在原来的基础上很快的产生新的对象;多态是同一个方法调用,针对不同的对象有不同的反应,这方便了程序的设计

  封装、继承、多态就是面向对象程序设计的三大基石。 通过封装,我们可以将现实世界中的数据和对数据进行操作的动作捆绑在一起形成类,然后再通过类定义对象,很好地实现了对现实世界事物的抽象和描述;通过继承,可以在旧类型的基础上快速派生得到新的类型,很好地实现了设计和代码的复用;同时多态机制保证了在继承的同时,还有机会对已有行为进行重新定义,满足了不断出现的新需求的需要

        (1) 容易设计和实现

        面向对象思想强调的是从客观存在的事物(对象)出发来认识问题和分析解决问题,因为这种方式更加符合我们认识事物的规律,所以大大降低了问题的理解难度,而面向对象思想所应用的封装、继承、多态都是符合人类日常的思维习惯的,所以使用面向对象思想设计的程序结构清晰、更容易设计和实现

        (2) 复用设计和代码,开发效率和系统质量都得到了提高

        面向对象思想的继承和多态,强调了程序设计和代码的重用,这在设计新系统的时候,可以最大限度地重用已有的、经过大量实践检验的设计和代码,使得系统能够满足新的业务需求并具有较高的质量。同时,因为可以复用以前的设计和代码,所以大大提高了开发效率

        (2) 容易扩展

        开发大型系统的时候,最担心的即使需求的变更以及对系统进行扩展,利用面向对象思想的封装、继承和多态,可以设计出“高内聚、低耦合”的系统结构,可以让系统更加灵活、更容易扩展,从而轻松应对系统的扩展需求,降低维护成本

01 面向过程(Porcess-oriented programing)

面向过程的编程思想是一种底层的思维

当遇到问题的时候,先分析解决问题需要的步骤,然后使用函数把这些步骤一步一步的实现,最后依次调用这些函数,问题就解决了


例子:在开发板的屏幕上面显示一个矩形
(1) 初始化屏幕

        lcd_init() {

                open  // 打开屏幕文件

                mmap  // 文件映射到内存

        }

(2) 画矩形

        lcd_draw_rect() {

                // 矩形由很多点组成

                for () {

                        for () {

                                ......

                        }

                }

        }


(3) 关闭屏幕

        lcd_close() {

                // 解映射

                // 关闭文件

        }

02 面向对象编程思想(Object oriented programing)

面向对象编程思想是一种上层思维


当遇到问题的时候,先把问题本身分解为组成问题的各个对象,再去描述每一个对象的类型(根据问题的本身去思考对象应该具有的行为和属性)

建立对象的目的并不是为了完成某一个单纯的步骤,而是为了描述对象在整个问题中的属性和行为,然后发消息给各个对象,使用各个对象单独或者协同其他对象完成一系列任务


例子:在开发板的屏幕上面显示一个矩形
(1) 要有一个开发板屏幕对象

struct Screen {
    属性:
        宽度
        高度
        ...
        文件描述符
        映射后的首地址
    行为:
        初始化() {
            打开屏幕
            映射内存
        }
        画点();
        清屏();
        关闭();
        ......
};

(2) 要有一个矩形对象

struct Rect {
    属性:
        大小(宽*高)
        位置
        颜色
    行为:
        初始化();
        设置大小();
        设置位置();
        设置颜色();    
        获取位置();
        .....
};

=====================================================================

struct Size {           
    属性:
        int width;
        int hight;
    行为:
        改变大小();
        获取大小();
};
                

struct Color {         
    属性:
        int color;
        char r;
        char g;
        char b;           
    行为:
        改变颜色();
        返回颜色();
};

                
struct Point {          
    属性:
        int x;
        int y;          
    行为:
        改变位置();
        返回位置();
};
=======================================================================
struct Rect {       
    属性:
        Size s; // 描述矩形的大小
        Point p; // 描述矩形的位置
        Color c; // 描述矩形的颜色        
    行为:
        初始化()
        设置大小()
        设置位置()
        设置颜色()
        获取位置()
        ......
        show(Screen *s); // 先使用指针传参
};

(3) 让屏幕对象和矩形对象协同工作,完成任务

Screen s; // s就是一个屏幕对象(变量)
s.初始化();

Rect r; // r就是一个矩形对象(变量)
r.初始化/设置属性();

//=================================   

第一种:矩形有一个显示功能,可以显示到指定的屏幕
r.show(&s);

第二种:屏幕有一个显示矩形的功能
s.draw(&r);

从上面的问题中,我们可以得到一个结果,面向对象和面向过程并不是相互独立的,而是相互包含的,在面向对象的这种思维中是包含了面向过程的,而面向过程也是可以包含面向对象的
 

程序设计语言的目的就是帮助我们使用代码来表达自己的思想:
        如:
                C语言能够完全的表达面向过程的编程思想,所以我们把C语言叫做面向过程的编程语言
                C++是在C语言的基础上发展而来的,所以C++肯定也支持面向过程的思想
                但是C++在C的基础上增加了许多特性,能够完整的表达面向对象的思想,所以我们把C++叫做面向对象的编程语言

03 如何实现面向对象编程

面向对象这种思想有三大基本特征:

        封装(Encapsulation):把数据和操作放在一起,升级和维护更加简单
        继承(inheritance):实现代码重用,提高开发效率
        多态(ploymorphism):实现接口重用,一个相同的接口,可以实现多种功能
        抽象


三大基本特征中,最基础的特征就是封装,要理解封装,要知道两个基本的概念:

        对象(Object)

                有物体、东西的意思,现实世界中一切都是对象(是一个具体存在的实体)

                        例子:

                                旺财

                                        属性:颜色、年龄、体重......

                                        行为:狗叫、吃东西、添......

                任何对象都有属性和行为

        

        同时,任何对象都属于某一个类型,如:旺财是属于“狗”类型,它不是“猫”


        类(class)

                一个类型就是一个概念,类型描述了一类事物共同的特征和行为

                        如:

                                “狗” 是一个概念(类型),而“猫”是另一个概念

                在C++中,我们使用类定义(描述)自己的数据类型,通过定义新的数据类型,来反映待解决问题中的各种概念

                我们一般先定义一个类类型,再使用这个类型去创建变量(实例化),该 "类" 类型的变量就是一个对象,对象中拥有类中定义的所有属性和行为

                

                所以:类就是类型,是一个抽象的概念,而对象是一个具体存在的实在!

                        如:我们所在的这栋楼就是一个对象,修建这栋楼时的设计图纸就是它的类型


如何定义一个类(类型)呢?

基本语法:

class 类名 {
    private:
        私有的属性和行为
    public:
        公用的属性和行为
    protected:
        保护的属性和行为
};

在class中,使用变量来描述对象的属性,叫做成员变量
在class中,使用函数来描述对象的行为,叫做成员函数

访问修饰词(限定了类成员在不同位置的访问权限):

        private:私有的
                private修饰的成员(函数和变量),称为私有成员,只有本类的成员函数和友元函数才能访问


        public:公有的
                public修饰的成员(函数和变量),称为公有成员,在任何地方都可以直接访问
                任何地方:成员函数中,全局函数,主函数...

        protected:保护的
                protected修饰的成员,称为保护成员,只有本类的成员函数和友元函数以及它的派生类才能访问

        对于类,一般是隐藏属性,公开访问接口


注意事项:

(1) 默认情况下(不加访问修饰词),class中的所有成员都是private的,外界无法直接访问

        所以一个合理的类的设计,应该公开其接口(函数),而隐藏内部实现(属性)

(2) 在定义类的时候,所以的class都可以被替换为struct,代码不会出错(能在class中用的,都能在struct中使用)

        但是class表示的是一个类,而struct表示的是一种数据结构

        class和struct最大的区别是,默认情况下,struct中所有的成员都是public的


(3) 在写类的时候,一般一个标准的类都分为两个文件(.h .hpp/.cpp)

        在头文件中声明类型和函数(.h/.hpp)
        在源文件中实现函数(.cpp)

(4) 关于类名和成员变量名
      
  类名的首字母一般大写,一看就知道这是一个类型
        成员变量名最好有所标识

            如:
                m_age;    // m是member的缩写
                _age;
                age_;

(5)
 如果类的成员函数在类中声明,在类外定义,则在定义的时候需要加上作用域运算符
        如:

                返回值类型  类名::函数名(参数列表) {

                        函数体;

                }

                

                void Dog::SetInfo(const char *str, int age) {

                        strcpy(m_name, str);

                        m_age = age;

                }

(6)
 如果类的成员函数,在类中定义,而不是把声明和定义分开,这种情况成员函数自动成为内联函数(inline)

class Test {
    public:
        void fun() { // fun函数默认声明为内联函数
            ...
        }
};

        注意:如果类的成员函数,手动声明为inline,那么在调用这个函数的文件中必须有这个函数的定义

(7) 只读成员函数(const 成员函数)

        在C++成员函数形式参数列表的后面,可以加一个const来修饰这个函数

如:
返回值类型 函数名(参数列表) const {
    ...
}
or          
返回值类型 类名::函数名(参数列表) const {
    ...
}

表示这个成员函数,是只读成员函数,该函数内可以使用非const成员变量,只能读取成员变量的值,保证不修改成员变量的值(增强程序的健壮性,防止程序员在函数中对成员变量的值进行误修改)

如果在只读成员函数中,修改了成员变量的值(或者调用了非const函数),编译器就会报错

#include <iostream>
#include <string.h>

using namespace std;

// 自己定义的类型
class Dog {
    private:
        char m_name[32];
        int m_age;
    public:
        // 成员函数
        void setInfo(const char *name = "wangcai", int age = 3);

        void GouJiao() const { // const函数,不能修改成员属性
            cout << "wangwangwang!" << endl;
            cout << "my name is " << m_name << endl; // 在成员函数中访问成员属性
            // Tian();  // const函数不能调用非const函数
        }

        void Tian() {
            cout << "你是一个靓仔!" << endl;
            m_age++;
        }
};

// 在类外定义的时候需要加作用域
void Dog::setInfo(const char *name, int age) {
    m_age = age;
    strcpy(m_name,name);
}

void fun(Dog wc) {
    // cout << wc.m_name << endl; // 只有本类的成员函数和友元函数才能访问
}

int main() {

    Dog wc; // 利用Dog类型实例化一个对象

    wc.setInfo("wangcai", 3);
    wc.GouJiao();
    wc.Tian();

    // cout << wc.m_name << endl; // 只有本类的成员函数和友元函数才能访问

    // const Dog wc2; // ERROR 自动生成的构造函数函数体为空且没有参数属性
                    // 定义了一个不知道内部状态的常量,没有任何意义

    // const int a; // ERROR

    return 0;
}

权限只能降不能升
const成员函数中,只能调用const成员函数
非const成员函数中,既可以调用const函数,又可以调用非const函数
            
const 对象只能调用const成员函数

非const对象,既能调用const函数,又能调用非const函数

const int a = 1;
const int aa = 2; 
int b = a; // OK  调用拷贝构造函数创建对象b
b = aa; // OK  调用赋值运算符重载
const int c = a; // OK
c = aa; // OK
==========================================================================
const指针能够指向普通对象,也能够指向const对象

int a = 100; // 普通对象
const int b = 100; // const常量
    
const int *p = &a; // YES
const int *q = &b; // YES

int *pp = &a; // YES
int *qq = &b; // ERROR 权限只能降,不能升
===========================================================================
关于const修饰指针:主要看const修饰什么

const int *p; // 忽略类型名称:const *p  const是修饰 *p 这个对象,*p不能作为左值
*p = 1024; // ERROR
p = &a; // YES

int const *p; // 忽略类型名称:const *p

int *const p; // 忽略类型名称:*const p  const修饰p本身,p不能作为左值

const int* const p; // 忽略类型名称:const * const p
*p = 1024; // ERROR
p = &a; // ERROR

04 练习

使用C++的方法(面向对象),在开发板上面显示一个矩形

        a. 显示矩形是屏幕的一个行为
                一般不会这样做,如果屏幕需要显示其他的形状、图片.....
                所有的东西都会写到屏幕类中,让屏幕对象很臃肿....

        b. 矩形有一个行为是显示到指定的屏幕(显示的这个动作是矩形本身的行为)

main.cpp

#include <iostream>
#include "rect.hpp"
#include "screen.hpp"

using namespace std;

int main() {

    Rect r; // 实例化一个矩形
    r.setColor(0x00ff0000);
    r.setPoint(-200, -200);
    r.setSize(300, 250);
    // r.show();

    Screen s; // 实例化一个屏幕
    s.init();
    s.clearScreen();

    // a.屏幕有一个功能,是显示矩形
    // s.drawRect(r);

    // b.矩形可以显示到指定的屏幕
    r.showScreen(&s);
    
    sleep(3);
    r.setColor(0x0000ff00);
    r.setPoint(300, 50);
    r.setSize(100, 80);
    // r.show();

    // a.屏幕有一个功能,是显示矩形
    // s.drawRect(r);

    // b.矩形可以显示到指定的屏幕
    r.showScreen(&s);

    s.close();

    return 0;
}

point.hpp        位置类的描述

#ifndef __POINT_HPP__
#define __POINT_HPP__

// 位置类的描述
class Point {
    private:
        // 位置在屏幕的坐标值
        int m_x;
        int m_y;
    public:
        // 设置位置
        void setPoint(int x, int y);
        // 获取位置
        int getX() const;
        int getY() const;
};

#endif

point.cpp

#include "point.hpp"

// 设置位置
void Point::setPoint(int x, int y) {
    m_x = x;
    m_y = y;
}

// 获取位置
int Point::getX() const {
    return m_x;
}

int Point::getY() const {
    return m_y;
}

size.hpp        描述一个尺寸类型

#ifndef __SIZE_HPP__
#define __SIZE_HPP__

// 描述一个尺寸类型
class Size {
    private:
        int m_w = 0; // 宽
        int m_h = 0; // 高
    public:
        // 设置尺寸
        void setSize(int w = 0, int h = 0);
        void setSize(Size s);
        // 获取
        int getW() const;
        int getH() const;
};

#endif

size.cpp

#include "size.hpp"

// 设置尺寸
void Size::setSize(int w, int h) {
    m_w = w;
    m_h = h;
}

void Size::setSize(Size s) {
    m_w = s.m_w;
    m_h = s.m_h;
}

// 获取
int Size::getW() const {
    return m_w;
}

int Size::getH() const {
    return m_h;
}

color.hpp        颜色类的描述

#ifndef __COLOR_HPP__
#define __COLOR_HPP__

// 颜色类的描述
class Color {
    private:
        int m_color;
        char m_r;
        char m_g;
        char m_b;
    public:
        void setColor(int color);
        void setColor(char r, char g, char b);
        void setColor(Color c);

        int getColor() const;
};

#endif

color.cpp

#include "color.hpp"

// 设置颜色
void Color::setColor(int color) {
    m_color = color;
    m_r = 0xff & (color >> 16);
    m_g = 0xff & (color >> 8);
    m_b = 0xff & (color);
}

// 设置颜色
void Color::setColor(char r, char g, char b) {
    m_r = r;
    m_g = g;
    m_b = b;
    m_color = r << 16 | g << 8 | b;
}

// 设置颜色
void Color::setColor(Color c) {
    m_color = c.m_color;
    m_r = c.m_r;
    m_g = c.m_g;
    m_b = c.m_b;
}

// 获取颜色
int Color::getColor() const {
    return m_color;
}

rect.hpp        描述矩形类

#ifndef __RECT_HPP__
#define __RECT_HPP__

#include <iostream>
#include "size.hpp"
#include "point.hpp"
#include "color.hpp"
#include "screen.hpp"

using namespace std;

class Screen; // 前向声明,防止头文件递归展开

// 描述矩形类
class Rect {
    private:
        // 尺寸,位置,颜色
        Size m_size; // 成员对象
        Point m_point;
        Color m_color;
    public:
        // 设置大小  宽,高
        void setSize(int w, int h);
        // 设置位置   左上角坐标
        void setPoint(int x, int y);
        // 设置颜色
        void setColor(int color);
        void setColor(Color c);

        // 获取大小 
        Size getSize() const;

        // 获取位置
        Point getPoint() const;

        // 获取颜色
        Color getColor() const;
        void getColor(int *color) const; // 用户提供一个可用的空间

        void show();

        // 矩形可以显示到指定的屏幕
        void showScreen(Screen *s); // 建议先使用指针传参,不然第二次显示会段错误!!!
};

#endif

rect.cpp

#include "rect.hpp"

// 设置大小
void Rect::setSize(int w, int h) {
    m_size.setSize(w, h);
}

// 设置位置
void Rect::setPoint(int x, int y) {
    m_point.setPoint(x, y);
}

// 设置颜色
void Rect::setColor(int color) {
    // m_color.m_color = color; // 不能在Rect的成员函数中访问Color的私有成员
    m_color.setColor(color);
}

void Rect::setColor(Color c) {
    // m_color = c; // 不能在Rect的成员函数中访问Color的私有成员
    m_color.setColor(c.getColor());
}

// 获取大小
Size Rect::getSize() const {
    return m_size;
}

// 获取位置
Point Rect::getPoint() const {
    return m_point;
}

// 获取颜色
Color Rect::getColor() const {
    return m_color;
}

void Rect::getColor(int *color) const {
    *color = m_color.getColor();
}

void Rect::show() {
    cout << "rect:m_Point(" << m_point.getX() << "," << m_point.getY() << ")" <<endl;
    cout << "rect:m_size(" << m_size.getW() << "," << m_size.getH() << ")" << endl;
    cout << "rect:m_color(0x" << std::hex << m_color.getColor() << std::dec << ")" << endl;
}

// 矩形可以显示到指定的屏幕
void Rect::showScreen(Screen *s) {
    // 把矩形形容的所有的点都显示为矩形中的颜色
    int x0 = getPoint().getX(); // 矩形的起点横坐标
    int y0 = getPoint().getY();

    int w = getSize().getW();
    int h = getSize().getH();
    
    int c;
    getColor(&c);

    for (int y = y0; y < y0 + h; y++) {
        for (int x = x0; x < x0 + w; x++) {
            s->drawPoint(x, y, c);
        }
    }
}

screen.hpp        描述屏幕类

#ifndef __SCREEN_HPP__
#define __SCREEN_HPP__

#include "size.hpp"
#include "rect.hpp" // 头文件不能相互包含,相互保护就会递归展开
#include "color.hpp"
#include "point.hpp"

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <errno.h>
#include <sys/mman.h>
#include <unistd.h>

using namespace std;

class Rect; // 前向声明,防止头文件递归展开

// 屏幕类的描述
class Screen {
    private:
        // 屏幕的尺寸
        Size m_size;
        char m_name[32]; // 路径名
        int m_fd;
        int *m_plcd;
    public:
        // 初始化
        void init(const char *name = "/dev/fb0", int w = 800, int h = 480);
        // 关闭
        void close();
        // 清屏
        void clearScreen(int color = 0x00ffffff);
        void clearScreen(Color c);
        // 画点
        void drawPoint(int x, int y, int color);
        void drawPoint(Point p, Color c);

        // 屏幕有一个行为是显示矩形
        void drawRect(Rect r);
};

#endif

screen.cpp

#include "screen.hpp"

// 初始化
void Screen::init(const char *name, int w, int h) {
    m_size.setSize(w, h);
    strcpy(m_name, name);
    m_fd = open(m_name, O_RDWR);
    if (-1 == m_fd) {
        perror("open lcd failed");
        exit(0);
    }
    m_plcd = (int *)mmap(NULL, w * h * 4, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
    if (m_plcd == MAP_FAILED) {
        perror("mmap lcd failed");
        ::close(m_fd); // 调用无名空间中的close
        exit(0);
    }
}

// 关闭
void Screen::close() {
    // 解映射
    munmap(m_plcd, m_size.getW() * m_size.getH() * 4);
    // 关闭
    ::close(m_fd); // 调用无名空间中的close
}

// 清屏
void Screen::clearScreen(int color) {
    // 把所有的点显示为指定的颜色
    int w = m_size.getW();
    int h = m_size.getH();
    for (int y = 0; y < h; y++) { // 遍历每一行
        for (int x = 0; x < w; x++) { // 遍历每一列
            drawPoint(x, y, color);
        }
    }
}

void Screen::clearScreen(Color c) {
    int color = c.getColor();
    clearScreen(color);
}

// 画点
void Screen::drawPoint(int x, int y, int color) {
    if (x < 0 || x >= m_size.getW() || y < 0 || y >= m_size.getH()) {
        // cout << "point out of the Screen!" << endl;
        return;
    }
    *(m_plcd + m_size.getW() * y + x) = color;
}

void Screen::drawPoint(Point p, Color c) {
    drawPoint(p.getX(), p.getY(), c.getColor());
}

// 屏幕有一个行为是显示矩形
void Screen::drawRect(Rect r) {
    // 把矩形形容的所有的点都显示为矩形中的颜色
    int x0 = r.getPoint().getX(); // 矩形的起点横坐标
    int y0 = r.getPoint().getY();

    int w = r.getSize().getW();
    int h = r.getSize().getH();

    int c;
    r.getColor(&c);

    for (int y = y0; y < y0 + h; y++) {
        for (int x = x0; x < x0 + w; x++) {
            drawPoint(x, y, c);
        }
    }
}

在开发板上面显示一个圆

circle.hpp        描述圆类

#ifndef __CIRCLE_HPP__
#define __CIRCLE_HPP__

#include "size.hpp"
#include "point.hpp"
#include "color.hpp"
#include "screen.hpp"
#include <iostream>

using namespace std;

// 描述圆类
class Circle {
    private:
        // 半径大小,位置,颜色
        int m_r; // 外圆半径
        int m_r2; // 内圆半径,=0:用于实心圆
        Point m_point;
        Color m_color;
    public:
        // 设置半径大小
        void setR(int r1, int r2 = 0); // 默认为实心圆
        // 设置位置
        void setPoint(int x, int y);
        // 设置颜色
        void setColor(int color);
        void setColor(Color c);

        // 获取半径大小
        int getR() const;

        // 获取位置
        Point getPoint() const;

        // 获取颜色
        Color getColor() const;
        void getColor(int *color) const; // 用户提供一个可用的空间

        void show();

        // 圆可以显示到指定的屏幕
        void showScreen(Screen *s); // 建议先使用指针传参,不然第二次显示会段错误!!!
};

#endif

circle.cpp

#include "circle.hpp"

// 设置半径大小
void Circle::setR(int r1, int r2) {
    m_r = r1;
    m_r2 = r2;
}

// 设置位置
void Circle::setPoint(int x, int y) {
    m_point.setPoint(x, y);
}

// 设置颜色
void Circle::setColor(int color) {
    // m_color.m_color = color; // 不能在Rect的成员函数中访问Color的私有成员
    m_color.setColor(color);
}

void Circle::setColor(Color c) {
    // m_color = c; // 不能在Rect的成员函数中访问Color的私有成员
    m_color.setColor(c.getColor());
}

// 获取半径大小
int Circle::getR() const {
    return m_r;
}

// 获取位置
Point Circle::getPoint() const {
    return m_point;
}

// 获取颜色
Color Circle::getColor() const {
    return m_color;
}

void Circle::getColor(int *color) const {
    *color = m_color.getColor();
}

void Circle::show() {
    cout << "circle:r(" << m_r << ")" << endl;
    cout << "circle:r2(" << m_r2 << ")" << endl;
    cout << "circle:Point(" << m_point.getX() << "," << m_point.getY() << ")" << endl;
    cout << "circle:color(0x" << std::hex << m_color.getColor() << std::dec << ")" << endl;
}

// 圆可以显示到指定的屏幕
void Circle::showScreen(Screen *s) {
    // 如果内圆半径大于等于外圆的半径,则不显示
    if (m_r2 >= m_r) {
        return;
    }
    // 把圆形容的所有的点都显示为圆中的颜色
    int x0 = getPoint().getX(); // 圆心
    int y0 = getPoint().getY();

    int r = getR();
    
    int c;
    getColor(&c);

    for (int y = y0 - r; y <= y0 + r; y++) {
        for (int x = x0 - r; x <= x0 + r; x++) {
            if ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= r * r) {
                if (m_r2 != 0 && (x - x0) * (x - x0) + (y - y0) * (y - y0) <= m_r2 * m_r2) { // 内圆空心
                    continue;
                }
                s->drawPoint(x, y, c);
            }
        }
    }
}

main.cpp

#include <iostream>
#include "rect.hpp"
#include "screen.hpp"
#include "circle.hpp"

using namespace std;

int main() {

    Screen s; // 实例化一个屏幕
    s.init();
    s.clearScreen();

    Circle c;
    c.setR(100); // 设置圆的半径,实心圆
    c.setPoint(200, 300);
    c.setColor(0x000000ff);
    // c.show();

    // 圆可以显示到指定的屏幕
    c.showScreen(&s);

    sleep(3);

    c.setR(100, 50);
    c.setPoint(500, 300);
    c.setColor(0x0000ffff);

    // 圆可以显示到指定的屏幕
    c.showScreen(&s);

    s.close();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值