2、C++学习——面型对象
1、类&对象
C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。
类:
类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。
类中的数据和方法称为类的成员。函数(即方法)也为类的成员。
类定义是以关键字 class 开头,后跟类的名称。
如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Box //定义名为Box的类 { public: //确定了类成员的访问属性 double length; // 盒子的长度 double breadth; // 盒子的宽度 double height; // 盒子的高度 };
注:关键字 public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。也可以指定类的成员为 private 或 protected。
对象:
类提供了对象的蓝图,所以基本上,对象是根据类来创建的。
如对象声明:
Box Box1; // 声明 Box1,类型为 Box类
Box Box2; // 声明 Box2,类型为 Box类
类成员函数:
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Box //定义名为Box的类 { public: //确定了类成员的访问属性 double length; // 盒子的长度 double breadth; // 盒子的宽度 double acreage(void) //类成员函数,即方法 { return length*breadth; } }; int main() { class Box box; // 声明 box,类型为 Box double w; box.length = 1.1; box.breadth = 2.0; w = box.acreage(); //调用类成员函数 cout << "box 的面积:" << w <<endl; }
或:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Box { public: double length; double breadth; double acreage(void); // 类成员函数声明 }; double Box::acreage(void) //类成员函数定义 { return length*breadth; } int main() { class Box box; double w; box.length = 1.1; box.breadth = 2.0; w = box.acreage(); cout << "box 的面积:" << w <<endl; }
类访问修饰符:
数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员。类成员的访问限制是通过在类主体内部对各个区域标记 public、private、protected 来指定的,即为访问修饰符。成员和类的默认访问修饰符是 private。
公有(public)成员:公有成员在程序中类的外部是可访问的。您可以不使用任何成员函数来设置和获取公有变量的值。
私有(private)成员:私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
保护(protected)成员:保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。
注:1.private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
2.protected 成员可以被派生类访问。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Box { public: //设置公有成员 double length; double getbreadth( void ); private: //设置私有成员 double breadth; };
如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
box.length = 1.1; //可不使用成员函数设置长度,length 是公有的。 double Box:: getbreadth (void) // 成员函数定义 { return breadth; } box. getbreadth (1.1); //使用成员函数设置长度,breadth是私有的。
构造函数 & 析构函数:
类的构造函数是一种特殊的函数,在创建一个新的对象时调用执行。构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Line //基类 { public: Line(){ //构造函数,在创建新的对象时调用执行,即初始化 cout << "Object is being created" << endl; } ~Line(){ // 析构函数 cout << "Object is being deleted" << endl; } void setLength( double len ){ length = len; } double getLength( void ){ return length; } private: double length; }; // 程序的主函数 int main( ) { Line line; //实例化 line.setLength(6.0); // 设置长度 cout << "Length of line : " << line.getLength() <<endl; return 0; }
结果:
Object is being created
Length of line : 6
Object is being deleted
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
通过使用另一个同类型的对象来初始化新创建的对象。
复制对象把它作为参数传递给函数。
复制对象,并从函数返回这个对象。
如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Line //基类 { public: Line( int len ){ // 构造函数 cout << "调用构造函数" << endl; // 为指针分配内存 ptr = new int; *ptr = len; } Line( const Line &obj){ // 拷贝构造函数 cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl; ptr = new int; *ptr = *obj.ptr; // 拷贝值 } ~Line(){ // 析构函数 cout << "释放内存" << endl; delete ptr; } int getLength( void ){ return *ptr; } private: int *ptr; }; // 程序的主函数 int main( ) { Line line1(10); //实例化 Line line2 = line1; // 实例化,调用拷贝构造函数 cout << "line 大小 : " << line1.getLength() <<endl; cout << "line 大小 : " << line2.getLength() <<endl; return 0; }
结果:
调用构造函数
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
line 大小 : 10
释放内存
释放内存
友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员,但是友元函数并不是成员函数。
友元可以是一个函数,也可以是一个类。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Box //基类 { public: friend void printWidth( Box box ){ // printWidth() 不是任何类的成员函数 cout << "Width of box : " << box.width <<endl; //printWidth() 是 Box 的友元,它可以直接访问该类的任何成员。 } void setWidth( double wid ){ width = wid;} protected: double width; }; int main( ) // 程序的主函数 { Box box; //实例化 box.setWidth(10.0); // 使用成员函数设置宽度 printWidth( box ); // 使用友元函数输出宽度,printWidth() 不是任何类的成员函数 return 0; }
输出:Width of box : 10
内联函数
内联函数是通常与类一起使用。
如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方,而对于其他的函数,都是在运行时候才被替代,即以空间代价换时间的节省。提高函数调用的效率。
如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline。
1.在内联函数内不允许使用循环语句和开关语句;
2.内联函数的定义必须出现在内联函数第一次调用之前;
3.在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符。
例:
4.只有当函数只有 10 行甚至更少时才将其定义为内联函数。
5.对于比较短, 性能关键的函数, 鼓励使用内联。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; inline int Max(int x, int y) { return (x > y)? x : y; } int main( ) // 程序的主函数 { cout << "Max (20,10): " << Max(20,10) << endl; cout << "Max (0,200): " << Max(0,200) << endl; cout << "Max (100,1010): " << Max(100,1010) << endl; return 0; }
结果:
Max (20,10): 20
Max (0,200): 200
Max (100,1010): 1010
this 指针
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。因此,在成员函数内部,它可以用来指向调用对象。
友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Box //基类 { public: Box(double a=2.0, double b=2.0) // 构造函数定义 { cout <<"Constructor called." << endl; length = a; breadth = b; } double Volume() //成员函数 Volume { return length * breadth; } int compare(Box box) //成员函数 compare { return this->Volume() > box.Volume(); // 使用 this 指针,代表类 Box 类本身 } private: double length; // Length of a box double breadth; // Breadth of a box }; int main(void) { Box Box1(3.3, 1.2); // 实例 box1 Box Box2(8.5, 6.0); // 实例 box2 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++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,在使用指针之前,必须对指针进行初始化。
实例见 4、多态
类的静态成员
2、继承
继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。
继承代表了 是.. 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。
当创建一个类时,可继承一个已有的类的成员即可。
已有的类称为基类,新建的类称为派生类。
形式如下:
class derived-class: access-specifier base-class //继承格式
其中,访问修饰符 access-specifier 是 public、protected、private 其中的一个,默认为 private。
几乎不使用 protected 或 private 继承,通常使用 public 继承。
base-class 是之前定义过的某个类的名称。
注意:派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
根据访问权限总结出不同的访问类型,如下所示:
访问 public protected private
同一个类 yes yes yes
派生类 yes yes no
外部的类 yes no no
一个派生类继承了所有的基类方法,但下列情况除外:
基类的构造函数、析构函数和拷贝构造函数。
基类的重载运算符。
基类的友元函数。
多继承
多继承即一个子类可以有多个父类,它继承了多个父类的特性。
C++ 类可以从多个类继承成员,语法如下:
class <派生类名>: <继承方式1><基类名1>, <继承方式2><基类名2>,…
{
<派生类类体>
};
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Shape //基类Shape { public: void setwidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height; }; class PaintCost //基类PaintCost { public: int getCost(int area) { return area*2; } }; class Rectangle: public Shape, public PaintCost //派生类,多继承 { public: int getArea() { return (width*height); } }; int main(void) { Rectangle Rect; // 声明 Rect,类型为Rectangle,实例化 int area; Rect.setwidth(1); Rect.setHeight(2); area = Rect.getArea(); //计算总面积 cout << "总花费: " << Rect.getCost(area) << endl; return 0; }
3、函数重载、运算符重载
C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。选择最合适的重载函数或重载运算符的过程,称为重载决策。
函数重载
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 a[10]){cout << "字符串为:" << a << endl;} }; int main() { PrintData Prt; //实例化 Prt.print(1); // 输出整数 Prt.print(2.3); // 输出浮点数 char c[] = "Hello"; Prt.print(c); // 输出字符串 return 0; }
运算符重载
C++ 允许可以重定义或重载大部分内置的运算符;
格式:Box operator+(const Box&);
4、多态
当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
例:(基类Shape被派生为两个类)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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,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; //实例化, 指向类的指针 *shape Rectangle rec(10,7); //实例化 Triangle tri(10,5); //实例化 shape = &rec; // 存储矩形的地址,初始化指针 shape ->area(); // 调用矩形的求面积函数 area shape = &tri; // 存储三角形的地址,初始化指针 shape->area(); // 调用三角形的求面积函数 area return 0; }
输出:
Rectangle class area
Triangle class area
5、数据抽象
数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程(设计)技术。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Adder{ //基类 public: Adder(int i = 0){ //构造函数,初始化数据total,创建类的新对象时执行。 total = i; } void addNum(int number){ //对外接口 total += number; } int getTotal() { return total; } private: int total; //对外隐藏的数据 }; int main() { Adder a; //实例化 a.addNum(1); a.addNum(2); a.addNum(3); cout << a.getTotal() << endl; return 0; }
结果:6
6、数据封装
封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装引申出了另一个重要的 OOP 概念,即数据储蓄。数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
C++ 通过创建类来支持封装和数据隐藏(public、protected、private)。
设计策略:
通常情况下,通常设置类成员状态为私有(private),除非真的需要将其暴露,这样才能保证良好的封装性和安全性。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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( ) { class Adder a; //实例化 a.addNum(10); a.addNum(20); a.addNum(30); cout << "Total " << a.getTotal() <<endl; return 0; }
结果:60
注:私有成员 total 是对外隐藏的,用户不需要了解它,但它又是类能正常工作所必需的。
7、抽象类接口
抽象类接口描述了类的行为和功能,而不需要完成类的特定实现。(可理解为一种声明)
以用于在派生类中实现不同的功能。virtual 和 =0 来表示。
抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,派生类通过继承抽象基类,就把所有类似的操作都继承下来。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; class Shape //基类 { public: virtual int getArea() = 0; //抽象接口函数,,virtual 和 =0 来表示。 void setWidth(int w){ width = w; } void setHeight(int h){ height = h; } protected: int width; int height; }; class Rectangle: public Shape //派生类 Rectangle { public: int getArea() //定义抽象接口函数,计算面积,方式1 { return (width * height); } }; class Triangle: public Shape //派生类 Triangle { public: int getArea() //定义抽象接口函数,计算面积,方式2 { return (width * height)/2; } }; int main() { Rectangle Rect; //声明 Rect,类型为 Rectangle, 实例化 Triangle Tri; Rect.setWidth(1); Rect.setHeight(2); cout << "Total Rectangle area: " << Rect.getArea() << endl; // 输出对象的面积 Tri.setWidth(1); Tri.setHeight(2); cout << "Total Triangle area: " << Tri.getArea() << endl; // 输出对象的面积 return 0; }
8、模板
模板是创建泛型类或函数的蓝图或公式。
函数模板
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; template <typename T> inline T const& Max (T const& a, T const& b) //泛化函数模板Max,比较两个数大小 { return a < b ? b:a; } int main () { int i = 39; int j = 20; cout << "Max(i, j): " << Max(i, j) << endl; //直接调用Max,比较两个整数大小 double f1 = 13.5; double f2 = 20.7; cout << "Max(f1, f2): " << Max(f1, f2) << endl; //直接调用Max,比较两个浮点数大小 string s1 = "Hello"; string s2 = "World"; cout << "Max(s1, s2): " << Max(s1, s2) << endl; //直接调用Max,比较两个字符串大小 return 0; }
输出:
Max(i, j): 39
Max(f1, f2): 20.7
Max(s1, s2): World
类模板
泛型类声明的一般形式如下所示:
template <class type>
class class-name {
…
}
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; template<class T1,class T2=string> //泛型类 class show { public: void show1(T1 &a){cout<<"show1:"<<a<<endl;} void show2(T2 &a){cout<<"show2:"<<a<<endl;} }; int main() { show<int> sh; // int 类型 int temp1=5; string temp2="Hello,C++!"; sh.show1(temp1); sh.show2(temp2); return 0; }
9、多线程
多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。基于进程的多任务处理是程序的并发执行,基于线程的多任务处理是同一程序的片段的并发执行。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <pthread.h> // 必须的头文件 using namespace std; #define NUM_THREADS 5 void* say_hello(void* args) // 线程的运行函数 { cout << "Hello Runoob!" << endl; return 0; } int main() { pthread_t tids[NUM_THREADS]; // 定义线程的 id 变量,多个变量使用数组 for(int i = 0; i < NUM_THREADS; ++i) { //参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数 int ret = pthread_create(&tids[i], NULL, say_hello, NULL); if (ret != 0) { cout << "pthread_create error: error_code=" << ret << endl; } } //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来; pthread_exit(NULL); }
参考文献
[1] https://www.runoob.com/cplusplus/cpp-tutorial.html
安装环境
https://blog.csdn.net/y_universe/article/details/78151998