首页 > 编程语言 >c++类&对象(学习笔记)

c++类&对象(学习笔记)

时间:2024-03-18 21:47:11浏览次数:30  
标签:函数 对象 double 成员 笔记 int void c++ public

c++类&对象

类,用户定义的类型,类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员,函数在一个类中被称为类的成员。

c++类的定义

定义一个类,本质上是定义一个数据类型的蓝图

这书籍上并没有任何数据,但他定义了类的名称意味着什么,他定义了类的对象包括了什么,以及可以在这个对象上执行那些操作。

类中包含变量和方法。

image-20240317100058676

类定义是以关键字class开头,后面跟类的名称

class asd
{
    public:
     	int length;
    	int breadth;
    	int height;
}

关键字public确定了类成员的访问属性,在类对象作用域内,公告成员在类的外部是可访问的。

也可以指定类的成员private和protected。

定义c++对象

类提供了对象的蓝图,对象是根据类来创建的。

声明类的对象,就像声明基本类型的变量一样。


#include<iostream>
using namespace std;
class box
{
    public:
    double length;
    double breadth;
    double height;
    void set(double len,double bre,double hei);
    double get(void);
};
void box::set(double len,double bre,double hei)
{
    length = len;
    breadth=bre;
    height = hei;
}
double box::get(void)
{
    return length * breadth * height;
}
int main()

{
    box b1;
    box b2;
    box b3;
    double volume=0;
    
     b1.height=5.0;
     b1.length=6.0;
     b1.breadth=7.0;
     volume=b1.get();
    cout<<volume<<endl;

     b2.height=5.0;
     b2.length=6.0;
     b2.breadth=7.0;
     volume=b2.get();
     cout<<volume<<endl;

     b3.set(12.0,3.0,21.0);
    volume = b3.get();
    cout<<volume<<endl;

}

私有成员和受保护的成员不能直接使用直接成员访问运算符.来直接访问。

概念 描述
类成员函数 类的成员函数是指那些把定义和原型写在类内部的函数,就像类定义中的其他变量一样。
类访问修饰符 类成员可以被定义为public、private、proteted,默认情况下static定义为public,class定义为private。
析构函数&构造函数 类的构造函数时一种特殊的函数,在创建以恶搞新的对象时调用,类的析构函数也是一种特殊函数,在删除所创建的对象时被调用.
c++拷贝构造函数 拷贝构造函数,是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建地对象。
c++友元函数 友元函数可以访问类中private和protected成员。
c++内联函数 通过内联函数,编译器试图在调用函数的地方扩展函数体中的代码。
c++this指针 每个对象都有一种特殊的指针this,它指向对象本身。
c++指向类的指针 指向类的指针方式如同指向结构的指针,实际上,类可以看成是一个带有函数的结构
c++类的静态成员 类的数据成员和函数成员都可以被声明为静态

c++类成员函数

类的成员函数是指那些把定义和原型写在类内部的函数,就像类定义中的其他变量一样。

类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中所有成员。

成员函数可以定义在类定义内部,或者在类的外部使用范围解析运算符::来定义

::叫作用域区分符,指明一个函数或一个数据属于哪个类。

//成员函数在类内部
class asd
{
    public:
    	double length;
    	double height:
    	double breadth;
    	
    	double getvolumn(void)
        {
        	return length*height* breadth;    
        }
};
//在类外部使用范围解析运算符::来定义函数
class asd
{
    public:
   		double length;
    	double height:
    	double breadth;
    	
    	double getvolumn(void);
};
double asd::getvolumn(void)
{
   return length*height* breadth;   
}

::运算符之前必须使用类名 数据类型 类名 ::成员函数名

而调用成员函数实在对象上使用带你运算符.

double asd::getvolu(void)
{
   ......
}
asd myasd;
myasd.getvolu();

c++类访问修饰符

数据封装是面向对象编程的一个重要特点,防止函数直接访问类类型的内部成员。

类成员的访问限制是通过在类主体内部对各个区域标记public、private、protected来指定的。

一个类中,可以又多个访问修饰符标记区域。

成员和类的默认修饰符是private。

公有成员(public)

公有成员在程序中类的外部是可访问的,可以不适用任何成员函数来设置和获取公有变量的值。

私有成员(private)

私有成员变量或函数在类的外部是不可访问的,甚至不可查看。

只有类和友元函数可以访问私有成员。

image-20240317123809291

一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数。

#include<iostream>
using namespace std;
class asdd
{
public:
    double length;
    void setwidth(double wid);
    double getwidth(void);
    double width;  
};
double asdd::getwidth(void){
    return width;
}
void asdd::setwidth(double wid){
    width=wid;
}
int main()
{
    asdd aa;
    aa.length=10;
    cout<<aa.length<<endl;

    aa.setwidth(10.0);
    cout<<aa.getwidth()<<endl;
    return 0;

}

无法在主函数中直接给私有区域的变量赋值。

保护成员(protected)

protected成员变量或函数与private成员十分相似,但protected成员在派生类(子类)中是可以访问的。


#include<iostream>
using namespace std;
class box
{
    protected:
        double width;
};
class smallbox:box
{
    public:
        void setsmallwidth(double wid);
        double getsmallwidth(void);
};
double smallbox::getsmallwidth(void)
{
    return width;
}
void smallbox::setsmallwidth(double wid)
{
    width=wid;
}
int main()
{
    smallbox box;
    box.setsmallwidth(5.0);
    cout<<box.getsmallwidth()<<endl;
}

继承中的特点

有public、protected、private三种继承方式,它们相应的改变了基类成员的访问属性。

  • 1.public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
  • 2.protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private
  • 3.private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private

构造函数&析构函数

一种特殊的函数,构造函数在创建一个新的对象时调用,析构函数在删除所创建的对象时调用。

类的构造函数

类的一种特殊的成员函数,每次创建类的新对象时执行。

  • 构造函数的名称与类名完全相同,并不会返回任何类型,也不会返回void。
  • 构造函数可以用于为某些成员变量设置初始值。

image-20240317140158367

含参构造函数


#include<iostream>
using namespace std;
class Line
{
    public:
        void setlength(double len);
        double getlength(void);
        Line(double len);
        
    private:
        double length;
};

Line::Line(double len)
{
    cout<<len<<endl;
    length=len;
}
void Line::setlength(double len){

    length=len;
}
double Line::getlength(void){
    return length;
}
int main()
{
    Line line(10.0);
    cout<<line.getlength();
    line.setlength(6.0);
    cout<<line.getlength();
    return 0;
}

使用初始化列表来初始化字段

Line::Line(double len): length(len)
{
    cout<<len<<endl;
}

===
Line::Line(double len)
{
    length = len;
    cout<<len<<endl;
}
//假如有一个c类,具有多个字段x、y、z等需要进行初始化
c::c(double a,double b,double c):x(a),y(b),z(c)
{
    ....
}

类的析构函数

类是一种特殊的成员函数,它会在每次删除所建的对象时执行。

  • 析构函数的名称和类的名称完全相同,在前门加一个波浪号~作为前缀,它不会返回任何值,也不能带任何参数。
  • 析构函数有助于在跳出程序前释放资源。
#include<iostream>
using namespace std;
class Line
{
    public:
        void setlength(double len);
        double getlength(void);
        Line();
        ~Line();
    private:
    double length;
};
Line::Line(){
    cout<<"这是构造函数"<<endl;
}
Line::~Line(){
    cout<<"这是析构函数"<<endl;
}
void Line::setlength(double len){
    length=len;
}
double Line::getlength(void){
    return length;
}
int main()
{
    Line line;
    line.setlength(5.0);
    cout<<line.getlength()<<endl;
    return 0;
}

c++拷贝构造函数

c++友元函数

类的友元函数定义在类外部,但有权访问所有的私有成员和保护成员。

尽管友元函数的原型有在类的定义中出现过,但是友元函数不是成员函数。

  • 友元可以是一个函数,该函数被称为友元函数。
  • 友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字friend

class bos
{
    double width;
    public:
    	double length;
    	friend void printwidth(bos box);
    void setwidth(double wid);
};

声明类classtow的所有成员函数作为类classone的友元,需要在类classone的定义中放置

friend class classtwo;

#include<iostream>
using namespace std;
class Box
{
    double width;
public:
    friend void printwidth(Box box);
    void setwidth(double wid);
};
void Box::setwidth(double wid)
{
    width=wid;
}
void printwidth(Box box)
{
    cout<<box.width<<endl;
}
//因为printwidth不是任何类的成员函数,
//因为printwidth()是Box的友元它可以直接访问该类的任何成员
int main()
{
    Box box;
    box.setwidth(10.0);
    printwidth(box);
    return 0;   
}

友元类

#include<iostream>
using namespace std;
class Box
{
    double width;
    public:
        friend void printwidth(Box box);
        friend class BigBox;
        void setwidth(double wid);
};
class BigBox
{
    public:
        void print(int width,Box &box)
        {
            box.setwidth(width);
            cout<<box.width<<endl;
        }
};
void Box::setwidth(double wid)
{
    width=wid;
}
void printwidth(Box box)
{
    cout<<box.width<<endl;
}
int main()
{
    Box box;
    BigBox big;
    box.setwidth(10.0);
    printwidth(box);
    big.print(20,box);
    getchar();
    return 0;
}

c++内联函数

c++中的this指针

在c++中,每一个对象都能通过this指针来访问自己的地址。

this指针是所有成员函数的隐含参数。

在成员函数内部,它可以用来指向调用对象。

友元函数没有this指针,因为友元不是类的成员。

只有成员函数才有this指针

#include<iostream>
using namespace std;
class Box
{
    public:
        Box(double l=2.0,double b=2.0,double h=2.0)
        {
            cout<<"constructor called"<<endl;
            length=l;
            breadth=b;
            height=h;
        }
        double volume()
        {
            return length * breadth * height;
        }
        int compare(Box box)
        {
            return this->volume()>box.volume();
        }
    private:
        double length;
        double breadth;
        double height;
};

int main()
{
    Box Box1(3.3,1.2,1.5);
    Box Box2(8.5,6.0,2.0);
    if(Box1.compare(Box2))
    {
        cout<<"Box2 is smaller than Box1"<<endl;
    }else{
        cout<<"Box2 is equal to or larger than Box1"<<endl;
    }
    return 0;
}

c++中指向类的指针

c++类的静态成员

可以用static关键字来把类成员定义为静态的

当我们在声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。

image-20240317171205031

静态成员在类的所有对象中都是共享的。

如果不存在其他初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为0。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析计算符::来重新声明静态变量从而对他进行初始化。

#include<iostream>
using namespace std;
class Box
{
    public:
        static int objectConst;
        Box(double l=2.0,double b=2.0,double h=2.0)
        {
            cout<<"constructor called"<<endl;
            length=l;
            breadth=b;
            height=h;
            objectConst++;
        }
        double volume(){
            return length* breadth*height;
        }
        private:
            double length;
            double breadth;
            double height;
            
};
int Box::objectConst=0;
int main()
{
    Box box1(3.3,1.2,1.5);
    Box box2(8.5,6.0,2.0);
    cout<<Box::objectConst<<endl;
    return 0;
}

静态成员函数

  • 把函数成员声明看成静态,就可以把函数与类的任何特定对象独立开来。
  • 静态成员函数即使在类对象不存在的情况下,也能被调用。
  • 静态函数只要使用类名加范围解析运算符::就可以访问。
  • 静态成员函数只能访问静态成员数据、其他静态成员数据函数和类外部的其他函数。
  • 静态成员函数有一个类范围,他们不能访问this指针。
  • 可以使用静态成员函数来判断的某些对象是否已被创建。

静态成员函数和普通成员函数的区别

  • 静态成员函数没有this指针,只能访问静态成员(包括静态成员变量和静态成员函数)
  • 普通成员函数有this指针,可以访问类中任意成员:而静态成员没有this指针.

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      static int objectCount;		//静态成员变量
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次创建对象时增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      //静态成员函数
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};
 
// 初始化类 Box 的静态成员
int Box::objectCount = 0;
 
int main(void)
{
  
   // 在创建对象之前输出对象的总数
   cout << "Inital Stage Count: " << Box::getCount() << endl;
 
   Box Box1(3.3, 1.2, 1.5);    // 声明 box1
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2
 
   // 在创建对象之后输出对象的总数
   cout << "Final Stage Count: " << Box::getCount() << endl;
 
   return 0;
}

c++继承

继承、允许我们依据另一个类来定义一个类,使得创建和维护一个应用程序变得更容易,达到了重用代码功能和提高执行效率的效果。

创建一个类时,可以不编写新的数据成员和成员函数,而指定新建的类继承了一个已有的类的成员。

已有的类称为基类,新建的类称为派生类。

image-20240318164450774

基类&派生类

一个类可以派生自多个类,就是可以从多个基类继承数据和函数。

定义一个派生类,可以使用一个类派生列表(一个or多个基类)来指定基类。

#include<iostream>
using namespace std;
class shape
{
    public:
        void setwidth(int w)
        {
            width = w;
        }
        void setheight(int h)
        {
            height = h;
        }
    protected:
        int width;
        int height;
};
class rectangle : public shape
{
    public:
    int getarr()
    {
        return (height * width);
    }
};
int main()
{
    rectangle r;
    r.setwidth(5);
    r.setheight(5);
    cout<<r.getarr()<<endl;
    return 0;
}

访问控制和继承

派生类可以访问基类中的所有非私有成员(即public和protected)。

基类中声明一个为private的领域不能被派生类中的成员函数访问。

一个派生类继承了所有基类方法,下列除外:

  • 基类的构造函数、析构函数和拷贝函数。
  • 基类的重载运算符。
  • 基类的友元

继承类型

由访问修饰符来指定包括public、protected、private几种类型。

通常使用public继承。

  • 公有继承:当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,积累的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
  • 保护成员:当一个类派生自保护基类时,基类的公有和保护成员将称为派生类的保护成员。
  • 私有继承:当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

多继承

多继承即一个子类可以有多个父类,它继承了多个父类的特性。

#include<iostream>
using namespace std;
class shape
{
    public:
    void setwidth(int w)
    {
        width = w;
    }
    void setheight(int h)
    {
        height = h;
    }
    protected:
        int width;
        int height;
};
class painconst
{
    public:
        int getconst(int ans)
        {
            return ans * 70;
        }
};
class rectangle :public shape,public painconst
{
    public:
        int getarea()
        {
            return (width * height);
        }
};
int main()
{
    rectangle r;
    int ans;
    r.setwidth(5);
    r.setheight(7);
    ans= r.getarea();
    cout<<r.getarea()<<endl;
    cout<<ans<<endl;
    cout<<r.getconst(ans)<<endl;
    return 0;
}

c++重载运算符和重载函数

c++允许在同一个作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。

c++中的函数重载

在同一个作用域内,可以声明几个功能类似的同名函数

但是这些同名函数的形式参数(指参数的个数、类型或顺序)比然不同

不能仅通过返回类型的不同来重载函数

#include<iostream>
using namespace std;
class printdata
{
    public:
        void print(int i)
        {
            cout<<"整数为"<<i<<endl;
        }
        void print(double f)
        {
            cout<<"浮点数"<<f<<endl;
        }
        void print(char c[])
        {
            cout<<"字符串"<<c<<endl;
        }
};
int main()
{
    printdata p;
    p.print(5);
    p.print(3.14);
    char c[]="hello world";
    p.print(c);
    return 0;
}

c++中的运算符重载

重载的运算符是带有特殊名称的函数,函数名是由关键字operator和其后要重载的运算符符号构成的。

其他函数一样,重载运算符有一个返回类型和一个参数列表。

Box operator+(const Box&);

声明加法运算符用于把两个Box对象相加,返回最终的Box对象。

大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。

如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数

Box operator+(const Box&, const Box&);
#include<iostream>
using namespace std;
class Box
{
    public:
        double getvolume()
        {
            return length * breadth * height;
        }
        void setlength(double len)
        {
            length =len;
        }
        void setheight(double hen)
        {
            height = hen;
        }
        void setbreadth(double bre)
        {
            breadth = bre;
        }
        Box operator+(const Box& b)
        {
            Box box;
            box.length = this->length + b.length; 
            box.height = this->height+b.height;
            box.breadth = this->breadth+b.breadth;
            return box;
        }
    private:
        double length;
        double breadth;
        double height;
};
int main()
{
    Box box1;
    Box box2;
    Box box3;
    double volume=0;

    box1.setlength(6.0);
    box1.setbreadth(7.0);
    box1.setheight(8.0);

    box2.setlength(9.0);
    box2.setbreadth(10.0);
    box2.setheight(11.0);

    volume=box1.getvolume();
    cout<<volume<<endl;
    volume=box2.getvolume();
    cout<<volume<<endl;
    box3=box1+box2;
    volume=box3.getvolume();
    cout<<volume<<endl;
    return 0;
}

可重载运算符/不可重载运算符

双目算术运算符 + (加),-(减),*(乘),/(除),% (取模)
关系运算符 ==(等于),!= (不等于),< (小于),> (大于),<=(小于等于),>=(大于等于)
逻辑运算符 ||(逻辑或),&&(逻辑与),!(逻辑非)
单目运算符 + (正),-(负),*(指针),&(取地址)
自增自减运算符 ++(自增),--(自减)
位运算符 | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)
赋值运算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空间申请与释放 new, delete, new[ ] , delete[]
其他运算符 ()(函数调用),->(成员访问),,(逗号),[](下标)

c++多态

多态按照字面意思就是多种形态。

当类之间存在层次结构,并且类之间是通过继承关联时就会用到多态。

c++多态意味着调用成员函数时,会根据调用函数的对象来执行不同的函数。

#include<iostream>
using namespace std;
class shape
{
    public:
        shape(int a=0,int b=0)
        {
            width=a;
            height=b;
        }
        int area()
        {
            cout<<"parent class area:"<<endl;
            return 0;
        }
    protected:
        int width;
        int height;
};
class rectangle :public shape
{
    public:
        rectangle(int a=0,int b=0):shape(a,b){}
        int area(){
            cout<<"rectangle class area :"<<endl;
            return (width * height);
        }
};
class Triangle : public shape
{
    public:
        Triangle(int a=0,int b=0):shape(a,b){};
        int area()
        {
            cout<<"Triangle class area :"<<endl;
            return (width*height/2);
        }
};

int main()
{
    shape *shape;
    rectangle rec(10,7);
    Triangle tri(10,5);
    shape = &rec;
    shape->area();
    shape = &tri;
    shape->area();
    return 0;
}

错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。

有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。

让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual

#include<iostream>
using namespace std;
class shape
{
    public:
        shape(int a=0,int b=0)
        {
            width=a;
            height=b;
        }
       virtual int area()
        {
            cout<<"parent class area:"<<endl;
            return 0;
        }
    protected:
        int width;
        int height;
};
class rectangle :public shape
{
    public:
        rectangle(int a=0,int b=0):shape(a,b){}
        int area(){
            cout<<"rectangle class area :"<<endl;
            return (width * height);
        }
};
class Triangle : public shape
{
    public:
        Triangle(int a=0,int b=0):shape(a,b){};
        int area()
        {
            cout<<"Triangle class area :"<<endl;
            return (width*height/2);
        }
};

int main()
{
    shape *shape;
    rectangle rec(10,7);
    Triangle tri(10,5);
    shape = &rec;
    shape->area();
    shape = &tri;
    shape->area();
    return 0;
}

此时,编译器看的是指针的内容,而不是他的类型。

因此,由于tri和rec类的对象的地址存储在*shape中,所以会调用各自的area()函数。

正如看到的每个子类都有一个函数area()的独立实现。

这就是多态的一般使用方式。

虚函数

虚函数是基类中使用关键字virtual声明的函数。

在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用函数,这种操作被称为动态链接后期绑定

纯虚函数

在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。

class shape
{
    public:
    shape(int a=0,int b=0)
    {
        width=a;
        height=b;
    }
    virtual int area()=0;
    protected;
        int width:
    	int height:
}

=0告诉编译器,函数没有主体。

c++数据抽象

数据抽象是指向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不是呈现细节。

数据抽象是一种依赖于接口和实现分离的编程技术。

在c++中我们使用类来定义我们自己的抽象数据类型。

#include<iostream>
using namespace std;
int main()
{
    cout<<"hello world"<<endl;
    return 0;
}

不需要理解cout是如何在用户的屏幕上显示文本,只需要知道是公共接口即可,cout的底层实现可以自由改变。

访问标签强制抽象

在c++中我们使用访问标签来定义类的抽象接口,一个类可以包含0个或多个访问标签:

  • 使用公共标签定义的成员都可以访问该程序的所有部分。一个类型的数据抽象视图是由它的公共成员来定义的。
  • 使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。

访问标签出现的频率没有限制。每个访问标签制定了紧随其后的成员定义的访问级别。指定的访问级别会一直有效,直到遇到下一个访问标签或者遇到类主体的关闭右括号为止。

数据抽象的好处

数据抽象有两个重要的优势:

  • 类的内部受到保护,不会因无意的用户级错误导致对象状态受损。
  • 类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。

如果只在类的私有部分定义数据成员,编写该类的作者就可以随意更改数据。如果实现发生改变,则只需要检查类的代码,看看这个改变会导致哪些影响。如果数据是公有的,则任何直接访问旧表示形式的数据成员的函数都可能受到影响。

例子

#include<iostream>
using namespace std;
class adder
{
    public:
        adder(int i=0)
        {
            total=i;
        }
        void addNum(int num)
        {
            total+=num;
        }
        int gettotal()
        {
            return total;
        }
    private:
        int total;
};
int main()
{
    adder a;
    a.addNum(10);
    a.addNum(20);
    a.addNum(30);
    cout<<a.gettotal()<<endl;
    return 0;
}

上面的类把数字加起来,并返回总和。

公有成员addnum和gettotal是对外的接口,用户需要知道他们以便使用类。

私有成员total是用户不需要了解的,但有是类能正常工作所必须的。

设计策略

抽象把代码分离为接口和实现。

所以在设计组件时,必须保持接口独立于实现,这样,如果改变底层实现,接口也将保持不变。

在这种情况下,不管任何程序使用接口接口都不会受到影响,只需要将最新的实现重新编译即可。

C++ 数据封装

所有的 C++ 程序都有以下两个基本要素

  • 程序语句(代码):这是程序中执行动作的部分,它们被称为函数。
  • 程序数据:数据是程序的信息,会受到程序函数的影响。

封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。

数据封装引申出了另一个重要的 OOP 概念,即数据隐藏

数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。

C++ 通过创建来支持封装和数据隐藏(public、protected、private)。

我们已经知道,类包含私有成员(private)、保护成员(protected)和公有成员(public)成员。

默认情况下,在类中定义的所有项目都是私有的。这是实现封装的一种方式。

为了使类中的成员变成公有的(即,程序中的其他部分也能访问),必须在这些成员前使用 public 关键字进行声明。

所有定义在 public 标识符后边的变量或函数可以被程序中所有其他的函数访问。

把一个类定义为另一个类的友元类,会暴露实现细节,从而降低了封装性。

理想的做法是尽可能地对外隐藏每个类的实现细节。

数据封装的实例

C++ 程序中,任何带有公有和私有成员的类都可以作为数据封装和数据抽象的实例。

#include <iostream>
using namespace std;
 
class Adder{
   public:
      // 构造函数
      Adder(int i = 0)
      {
        total = i;
      }
      // 对外的接口
      void addNum(int number)
      {
          total += number;
      }
      // 对外的接口
      int getTotal()
      {
          return total;
      };
   private:
      // 对外隐藏的数据
      int total;
};
int main( )
{
   Adder a;
   
   a.addNum(10);
   a.addNum(20);
   a.addNum(30);
 
   cout << "Total " << a.getTotal() <<endl;
   return 0;
}

上面的类把数字相加,并返回总和。公有成员 addNumgetTotal 是对外的接口,用户需要知道它们以便使用类。私有成员 total 是对外隐藏的,用户不需要了解它,但它又是类能正常工作所必需的。

设计策略

通常情况下,我们都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证良好的封装性

这通常应用于数据成员,但它同样适用于所有成员,包括虚函数。

C++ 接口(抽象类)

接口描述了类的行为和功能,而不需要完成类的特定实现。

C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。

如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。

纯虚函数是通过在声明中使用 "= 0" 来指定的

class Box
{
   public:
      // 纯虚函数
      virtual double getVolume() = 0;
   private:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
};

设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。

抽象类不能被用于实例化对象,它只能作为接口使用。

如果试图实例化一个抽象类的对象,会导致编译错误。

因此,如果一个 ABC 的子类需要被实例化,则必须实现每个虚函数,这也意味着 C++ 支持使用 ABC 声明接口。

如果没有在派生类中重写纯虚函数,就尝试实例化该类的对象,会导致编译错误。

可用于实例化对象的类被称为具体类

抽象类的实例

基类 Shape 提供了一个接口 getArea(),在两个派生类 Rectangle 和 Triangle 中分别实现了 getArea()

#include <iostream>
 
using namespace std;
 
// 基类
class Shape 
{
public:
   // 提供接口框架的纯虚函数
   virtual int getArea() = 0;
   void setWidth(int w)
   {
      width = w;
   }
   void setHeight(int h)
   {
      height = h;
   }
protected:
   int width;
   int height;
};
 
// 派生类
class Rectangle: public Shape
{
public:
   int getArea()
   { 
      return (width * height); 
   }
};
class Triangle: public Shape
{
public:
   int getArea()
   { 
      return (width * height)/2; 
   }
};
 
int main(void)
{
   Rectangle Rect;
   Triangle  Tri;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
   // 输出对象的面积
   cout << "Total Rectangle area: " << Rect.getArea() << endl;
 
   Tri.setWidth(5);
   Tri.setHeight(7);
   // 输出对象的面积
   cout << "Total Triangle area: " << Tri.getArea() << endl; 
 
   return 0;
}
Total Rectangle area: 35
Total Triangle area: 17

一个抽象类是定义一个接口 getArea(),两个派生类是通过不同的计算面积的算法来实现这个相同的函数。

设计策略

面向对象的系统可能会使用一个抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。

然后,派生类通过继承抽象基类,就把所有类似的操作都继承下来。

外部应用程序提供的功能(即公有函数)在抽象基类中是以纯虚函数的形式存在的。

这些纯虚函数在相应的派生类中被实现。

这个架构也使得新的应用程序可以很容易地被添加到系统中,即使是在系统被定义之后依然可以如此。

C++面向对象 (yuque.com)

跟着这位up主的文章来学习的!!!太强了

标签:函数,对象,double,成员,笔记,int,void,c++,public
From: https://www.cnblogs.com/157184lcy/p/18081493

相关文章

  • RabbitMQ-笔记
    RabbitMQ-笔记目录零、资料一、RabbitMQ常见的3种模型1.1、基本队列1.2、工作队列1.3、发布订阅1.3.1Fanout交换机1.3.2Dirct交换机1.3.3Topic交换机1.4消息转化器零、资料黑马-RabbitMQ快速入门一、RabbitMQ常见的3种模型1.基本队列2.工作队列3.发布订阅(解决前......
  • 亲子游戏【华为OD机试JAVA&Python&C++&JS题解】
    题目描述宝宝和妈妈参加亲子游戏,在一个二维矩阵(NN)的格子地图上,宝宝和妈妈抽签决定各自的位置,地图上每个格子有不同的糖果数量,部分格子有障碍物。游戏规则是妈妈必须在最短的时间(每个单位时间只能走一步)到达宝宝的位置,路上的所有糖果都可以拿走,不能走障碍物的格子,只能上下......
  • C++看程序写结果:调用一次Line类构造函数,执行几次Point类复制构造函数?
    C++看程序写结果:调用一次Line类构造函数,执行几次Point类复制构造函数?#include<iostream>#include<cmath>usingnamespacestd;classPoint{//Point类定义public:Point(intxx=0,intyy=0){x=xx;y=yy;}Point(Point&p);......
  • 【CSP考点回顾】C++标准库加速输入输出
    C++标准库加速输入输出ios_base::sync_with_stdio(false);:取消C++标准库(iostream)与C标准库(stdio)之间的同步。默认情况下,为了保证C++的cin、cout与C的stdin、stdout能够互相交换数据,它们之间会进行同步。这样做虽然安全,但会减慢IO操作的速度,因为每次IO操作都需要进行同步。......
  • Flask学习笔记
    Flask构成1.路由(Routing):Flask使用路由来定义URL和对应的处理函数。通过装饰器@app.route(),可以将一个URL映射到相应的处理函数上,从而实现请求的路由和处理。2.视图函数(ViewFunctions):视图函数是Flask中处理请求的核心组件,它接收请求并返回响应。视图函数通常被装饰器绑......
  • 学习笔记444—macbook软件卸载了图标还在launchpad上怎么办?一分钟搞定!
    macbook软件卸载了图标还在launchpad上怎么办?一分钟搞定!问题描述有时候,我们使用MacBook时没有从AppleStore安装软件,而是从一些非官方渠道安装的软件。这样我们将这些macbook软件卸载了之后,图标还会留在在launchpad(启动台)上,这些软件的图标无法删除,鼠标长按也不会出来叉叉。我们的......
  • JavaScript学习笔记6: 对象 - 字符串Stirng
    JS对象-字符串String字符串的创建方式<script>//字符串创建方式1varstr1=newString("str1");//字符串创建方式2varstr2="str2";</script>字符串属性&方法length属性<script>console.log("获取字符串的length属性");    con......
  • JavaScript学习笔记7: 对象 - 自定义对象&JSON
    JS对象-自定义对象&JSON自定义对象类似java的类Json的所有属性(key)需要用双引号包围,本质是字符串<script>    varuser={    name:"tom",    age:10,    gender:"male",    //eat:function(){}    //可以简写为    eat(){//自......
  • 09常用对象及api
    1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8">5<metaname="viewport"content="width=device-width,initial-scale=1.0">6<title>Document......
  • JavaScript学习笔记3: 数据类型,运算符,类型转换
    JS数据类型,运算符,类型转换利用typeof获取数据类型数字3的类型<script>console.log("3的类型:"+typeof3);</script>浮点数<script>console.log("3.14的类型:"+typeof3.14);</script>字符串<script>console.log("'......