晚上翻来覆去无法入睡,于是早起来继续之前没有完成的进一步学习操作。意外的是无意间打开抖音app,看了好一段时间才停下来,果然短视频的洗脑能力仍然是非常强悍的。

C++数据结构

C/C++数组允许定义可存储相同类型数据项变量,但是C++中另一种用户自定义的可用数据类型,允许用户存储不同类型的数据项。

结构用于表示一条记录。

定义结构

定义结构,必须使用struct语句。格式如下:

struct type_name{
member_type1 member_name;
member_type2 member_name;
member_type3 member_name;
.
.
} object_names;

访问结构成员

        访问结构成员,使用成员访问运算符(.)。

结构作为函数参数

        可以将结构作为函数参数,传参方式与其他类型的变量或指针类似。

指向结构的指针

        可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似。

        使用指向某个结构的指针访问结构的成员,必须使用->运算符。

typedef关键字

        创建别名。

C++类&对象

C++在C语言的基础上增加了面向对象编程,C++支持面向对象程序设计。类是C++的核心特性,通常被称为用户定义的类型。

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

C++类定义

        定义一个类,本质上是定义了一个数据类型的蓝图。实际上并没有定义任何数据,但它定义了类的名称意味着什么,也就是说,它定义了类的对象包括什么,以及可以在这个对象上执行什么操作。

        

class classname
{
Access specifies: //访问修饰符:private/public/protected
    Data members/variables;    //变量
    Member functions(){}    //方法
};    //分号结束一个类

        类定义以关键字class开头,后跟类的名称。类的主体包括在一对花括号之中。类定义后必须跟着一个分号或一个声明列表。

        在类对象作用域内,公共成员在类的外部都是可访问的。

定义C++对象

        类提供了对象的蓝图,所以,类是根据对象来创建的。声明类的对象,就像声明基本类型的变量一样。对象有它们各自的数据成员。

访问数据成员

        类的对象的公共数据成员可以使用直接成员访问运算符. 来访问。

#include<iostream>

using namespace std;

class Box
{
public:
    double length; //长度
    double breadth; //宽度
    double height; //高度
//成员函数声明
    double get(void);
    void set( double len, double bre, double hei );
};
//成员函数定义
double Box::get(void)
{
return length*breadth*height);
}

void Box::set(double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}

int main()
{
Box Box1;    //声明Box1,类型为Box
Box Box2;    //声明Box2,类型为Box
Box Box3;    //声明Box3,类型为Box
double volume = 0.0     //用于存储体积    

//box1详述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 13.0;

//box2详述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;


//box1的体积
volume = Box1.length*Box1.breadth*Box1.height;
cout << "Box1的体积:" << volume << endl;

//box2的体积
volume = Box2.length*Box2.breadth*Box2.height;
cout << "Box2的体积:" << volume << endl;


//box3详述

Box3.set(16.0,17.0,18.0);
volume = Box3.get();
cout << "Box3的体积:" << volume << endl;
return 0;
}

需要注意的是,私有成员和受保护的成员不能使用直接成员访问符(.)来进行访问。

类&对象详解

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

        类访问修饰符        类成员可以被定义为public、private、protected。默认情况下定义为private。

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

        C++拷贝构造函数        拷贝构造函数,是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。

        C++友元函数        友元函数可以访问类的private和protected成员。

        C++内联函数        通过内联函数,编译器试图在调用函数的地方扩展函数体中的代码。

        C++中的this指针        每个对象都有一个特殊的this指针,它指向对象本身。

        C++中指向类的指针        指向类的指针方式如同指向结构的指针。实际上,类可以看成是一个带有函数的结构。        

        C++类的静态成员        类的数据成员和函数成员都可以被声明为静态的。

C++继承

        面向对象的程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更加容易。同时也达到了重用代码功能和提高执行效率的结果。

        在创建一个类时,不需要重新编写新的数据成员和成员函数,只需要指定新建的类继承一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。

   

基类&派生类

        一个类可以派生自多个类,可以从多个基类继承数据和函数。定义一个派生类,需要使用一个派生类列表来指定基类。派生列表以一个或多个基类命名。

访问控制和继承

        派生类可以访问 基类中所有的非私有成员。因此基类成员若不想被访问,则应在基类中声明private。

继承类型

        当一个类派生自基类,该基类可以被继承为public、protected或private几种类型。继承类型是通过上面讲解的访问修饰符access-specifier来指定的。

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

        保护继承:当一个类派生自保护基类时,基类的公有和保护成员都将称为派生类的保护成员。

        私有继承:当一个类派生自私有基类时,基类的公有和保护成员都将成为派生类的私有成员。

多继承

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

#include<iostream>

using namespace std;

//基类 Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};

//基类 PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area*70;
}
};

//派生类
class Rectangle: public Shape, public PainCost
{
public:
int getArea()
{
return (width*height);
}
};

int main(void)
{
Rectangle Rect;
int area;

Rect.setWidth(5);
Rect.setHeight(7);

area = Rect.getArea();

//输出对象面积
cout<<"Total area:"<<Rect.getArea()<<endl;

//输出总花费
cout<<"Total paint cost:$"<<Rect.getCost(area)<<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(void)
{
printData pd;
//输出整数
pd.print(5);
//输出浮点数
pd.print(2.0);
//输出字符串
char c[] = 'Hello, C++";
pd.print(c);

return 0;
}

C++中运算符重载

用户可以重定义或重载大部分C++内置的运算符。

重载的运算符是带有特殊名称的函数,函数名是由关键字operator和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。

#include<iostream>
using namespace std;

class Box
{
public:

double getVolume(void)
{
return length*breadth*height;
}
void setlength(double len)
{
length = len;
}
void setBreadth(double bre)
{
breadth = bre;
}
void setHeight(double hei)
{
height = hei;
}
//重载+运算符,用于把两个Box对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length+b.length;
box.breadth = this->breadth+b.breadth;
box.height = this->height+b.height;
return box;
}
private:
double length; //长度
double breadth; //宽度
double height; //高度
};
//程序的主函数
int main()
{
Box Box1; //声明Box1,类型为Box
Box Box2; //声明Box2,类型为Box
Box Box3; //声明Box3,类型为Box
double volume=0.0 //把体积存储在该变量中

//box1详述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 13.0;

//box2详述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;


//box1的体积
volume = Box1.getVolume();
cout << "Box1的体积:" << volume << endl;

//box2的体积
volume = Box2.getVolume();
cout << "Box2的体积:" << volume << endl;

//把两个对象相加,得到Box3
Box3 = Box1 + Box2;
//Box3的体积
volume=Box3.getVolume();
cout<<"Volume of BOx3:"<<volume<<endl;

return 0;
}

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

        可重载:双目运算符;关系运算符;逻辑运算符;但目运算符;自增自减运算符;位运算符;赋值运算符;空间申请与释放;其他运算符

        不可重载:成员访问运算符;成员指针访问运算符;域运算符;长度运算符;条件运算符;预处理运算符

C++多态

多态按照字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++多态意味着调用成员时,会根据调用函数的对象的类型来执行不同的函数。

虚函数

        虚函数是基类中使用关键字virtual声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。我们想要的在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种擦欧总被称为动态链接,或后期绑定。而静态链接是在程序执行前就已经准备好了,属于早期绑定。

纯虚函数

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

        virtual int area()=0;

=0告诉编译器,上面的函数是纯虚函数,没有主体。

C++数据抽象

数据抽象是指向外界提供关键信息,病隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程(设计)技术。

在C++中,用户用类来定义我们自己的抽象数据类型(ADT)。可以适用类型iostream的cout对象来输出数据到标准输出。

访问标签强制抽象

        适用访问标签来定义类的抽象接口。一个类可以包含零个或多个访问标签:

        使用公共标签定义的成员都可以访问到该程序的所有部分。一个类型的数据抽象视图是由它的公共成员来定义的。

        使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。

数抽象的好处

        类的内部受到保护,不会因无意的用户级错误导致对象状态受损;

        类实现那可能随着时间的推移而发生变化,以便应对不断变化的需求,或者对那些要求不改变用户几代码的错误报告。

数据抽象的实例:

任何带有私有或者公有成员的类都可以作为数据抽象的实例。如:

#include<iostream>
using namespace std;

class Adder{
public:
//构造函数
Adder(int i = 0)
{
total = i;
}
//对外的接口
void addNum(int number)
{
total += number;
}
//对外的接口
int getTotal()
{
ireturn total;
}
private:
//对外隐藏的数据
int total;
};
int main()
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);

cout<<"Total" << a.getTotal() <<endl;
return 0;
}

设计策略

        抽象把代码分离为接口和实现。所以在设计组件时,必须保持接口独立于实现,这样,如果改变底层实现,接口也将保持不变。在这种情况下,不管任何程序使用接口,接口都不会受到影响,只需要将最新的实现重新编译即可。

C++数据封装

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

        程序语句(代码):这是程序中执行动作的部分,它们被称为函数;

        程序数据:数据是程序的信息,会受到程序的影响。

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

数据封装的实例

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;
}

设计策略

在通常情况下,我们都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证其良好的封装性。这通常应用于数据成员,但它同样适用于所有成员,包括虚函数。

C++接口(抽象类)

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

C++接口是使用抽象类实现的,抽象类与数据抽象不混淆,数据抽象是一个把实现细节与相关的数据分开的概念。如果类中至少有一个函数声明为纯虚函数,则这个类就是抽象类。

设计抽象类(ABC)的目的,是为了给其他类提供一个可以继承的适当的基本类。抽象类不能被用于实例化对象,它只能作为接口使用。如果试图实例化一个抽象类的对象,会产生编译错误。

因此,如果每个ABD的子类需要被实例化,就必须实现每个虚函数,这也以为着C++支持使用ABC声明接口。如果没有子啊派生类中重写虚函数,就尝试实例化该类的对象,就会导致编译错误。j可用于实例化的对象称为具体类。

抽象类的实例:

#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);
}
};
lass TriAngle public Shape
{
public:
int getArea()
{
return (width*height)/2;
}
};

int main(void)
{
Tectangle Rect;
TrAngle 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;
}

设计策略

面向对象的系统可能会使用一个抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,派生类通过继承抽象基类,就把所有类似的操作全都继承下来。

外部应用程序提供的功能(即公有函数)在抽象基类中是以纯虚函数的形式存在的。这些纯虚函数在相应的派生类中被实现。这个架构也使得新的应用程序可以很容易地被添加到系统中,即使是在系统被定义之后依然可以如此。

已经看到高级教程的前面的部分了。想来是真正学完了基础。