赞
踩
1. C++面向对象编程介绍
面向对象编程(Object Oriented Programming),简称OOP。
在传统的面向过程编程中,数据以及数据的相关操作函数都是分离的独立个体;
对象,如周围的一切其实都是对象;就编程角度,对象包括A)一系列属性(数据);B)一系列操作(函数)。
OOP提供了设计对象的功能,对象包括特性和行为,两者都囊括在一起,共同构成对象实体(即类实体);
对象实体,使程序更模块化,更易读易写,提升了代码重用到一个更高的层次;
对象提供了对数据操作的直接方法,定义了如何与对象进行交互,以及对象之间的交互;
更重要的是,OOP提供了更实用的概念:封装、继承、多态和抽象。
这篇主要讲述封装,即对象将数据及其操作函数封装到一个类实体中。
2. 类和类成员
C++提供了如char、int、long、float、double等基本数据类型,足够用来解决大多数哦相对简单的问题,但对于复杂的问题就比较困难了。
C++的一个实用特性是自定义数据类型;如之前的枚举和结构体:
枚举和结构体代表了传统的面向过程编程,它们只包含数据,如果需要访问此类型变量,必须提供自定义函数,并以该类型变量为参数:
在OOP世界中,我们希望自己的类型不仅包括数据,也包括操作数据的函数;C++中,通过class关键字来声明一个类。
2)类类似结构体,但它更多功能和更灵活:
类似结构体,类的声明没有分配内存,只是说明了类的结构;类的声明以分号结尾;
为了使用类,可以声明一个该类型的变量:
声明一个类的变量,也叫实例化一个类;该变量称作为类的一个实例,或者对象。
3)成员函数:
类不仅仅包含数据(成员变量),亦可以包含函数,称作成员函数:
成员函数可以类似访问成员变量,使用'.'来使用:
通常,类的成员变量,加一个前缀'm_'来区分:
3. 公有和私有
1)访问标识符:公有-public关键字,私有-private关键字
在上面的DateStruct的结构体,它的成员可以在main函数中访问,因为结构体的所有成员默认是公有的,即public;
公有成员在结构体和类中,程序的任何函数都可以访问;
但如果Date的成员没有public标识符,则会出现错误:
这个说明,类成员的默认是私有的,即private的;
私有的成员只能在类的定义之内的函数可以访问;
可以通过public关键字使成员变为公有的,即可访问了:
类和结构体的本质区别之一是类的成员可以使用标识符来控制它们的可访问性;
C++提供了3种不同的访问标识符:public、private、protect,分别是公有的、私有的和保护的:
4. 访问函数和封装
1)访问函数,也叫做读写函数;是读取和写入私有成员变量的值。
例如:
GetLength函数就是个访问函数。
访问函数有2种,即getter和setter:
将数据成员私有化,提供Getter和Setter来访问,即所谓的"封装" ;
2)封装
封装的思想就是将实现的细节隐藏,而暴露公有接口;
C++中的访问标识符,可以实现在类中的封装;通常是将所有的成员变量私有化;
尽管看起来访问成员变量的不直接,但使程序更有可重用性和可维护性;
A)封装实现,无论类的实现如何改变,只要对外的接口不发生变化即可。
如图上例,如果m_Value被重命名了,那么main函数中访问就会出错;
如果提供了m_Value的访问函数:
B)隐藏了类的实现,类的使用者只需知道公共的接口,就可以使用该类;
C)封装帮助防止意外的改变和误用;
D)对程序调试有很大的帮助,因为改变类的成员变量只用通过公共接口。
5. 构造函数Ⅰ
1)构造函数:是类的一种特殊的成员函数,当类被实例化时执行;通常用以初始化成员变量。
构造函数有明确的命名规则:A)函数名必须和类名一样;B)无返回类型(包括void)。
无参构造函数-不带参数的构造函数,是类的默认构造函数:
通常,类都包含一个默认的构造函数,可以初始化成员变量。
含参构造函数-含有参数的构造函数,可以对成员变量赋予指定的值;
以上的两个构造函数,类似重载函数;构造函数必须有唯一的前面(参数个数和参数类型)。
类亦可以只提供含参构造函数,没有默认构造函数:
6. 析构函数
析构函数是类的另一种特殊的函数,当类的对象销毁时调用;它和构造函数是成对出现的。
普通的简单类,一般不需要析构函数;因为C++会自动回收垃圾;
如果类中执行了某些动态内存分配,则需要显式定义析构函数,并释放回收垃圾;
析构函数的明确命名规则:A)函数名和类名一样,并前缀'~';B)不能带参数(即意味着只有一个析构函数);C)没有返回类型。
注意动态分配,必须提供析构函数,来回收分配的空间。
2)构造函数和析构函数的时序:
如上图所示,输出的依次是:simple的构造函数,pSimple的构造函数,最后是pSimple的析构函数。
构造函数和析构函数的时序是:Constructor First, Destructor Last。
7. 隐藏的'this'指针
如之前的例子中的this,是每个类的成员函数中隐藏的指针,它指向了类成员函数打交道的类的对象。
实用性:
1)当构造函数或成员函数中的参数名和成员变量名相同时,可以使用this来访问本类的成员变量;
2)可以使用this返回类的对象引用:
8. 构造函数Ⅱ
1)私有构造函数-如果不想类以外使用指定的构造函数,我们可以将它私有化。
类只能被实例化一次,称作为单一性;通常使用私有/保护构造函数进行的。
2)构造函数链和初始化
有时,一个构造函数所做的工作和另外的构造函数一样,只是增加了一些;
这样这个构造函数可以调用另外的构造函数,称作为构造函数链。如C#就支持这种格式,但C++不支持。
但构造函数可以调用类中的非构造函数,只是要注意这些非构造函数调用的成员,必须已经初始化。
通常的做法就是,定义一个公共的非构造函数,构造函数都调用它来初始化共同的;例如:
9. 类代码和头文件
1)在类的定义之外定义成员函数。
如之前的类定义,都是在类的定义中定义成员函数:
当类的定义越来越长和越来越复杂时,就显得臃肿,难以维护和操作;
幸运的是,C++提供了一种分离类的定义及其应用定义的方法,将类的成员函数在类的外面定义;格式是:类名::函数名
2)将类的定义放在头文件中:
头文件的使用可达到重用的效果;所以将类的定义放在头文件中,而成员函数放在.cpp中定义;而cpp的名字需和类的名字相同。
Date.h:
Date.cpp:
推荐分离类的定义中的成员函数到类外定义。
10. 常量类对象和成员函数
函数的参数可以为常量对象,如内置的基本数据类型一样,类对象也可以声明为常量,所有常量对象的变量必须在创建时初始化,其后不能修改。
上图3个错误,因为程序试图修改常量类对象的变量;
因为常量类对象不能调用非常量成员函数;
常量成员函数-保证不修改任何类变量或调用任何非常量函数。
为了使GetValue常量化,可以在其原型加个const关键字:
注意:
A) 常量成员函数在类外定义时,也必须加const关键字;
B) 任何常量成员函数试图修改类成员变量,或者调用非常量成员函数都是非法的,会产生编译错误。
C)构造函数不能常量化;
重载函数使用const和非const,是当返回类型不一样的时候。
11. 静态成员变量
在之前的程序中,静态表示变量的值在运行期间保持最新的值;
1)静态成员变量
在实例化两个对象,其包含的相同的成员变量;
静态成员变量是属于类的本身,是所有对象的共享变量;它的值是保持修改的最新值;
使用格式:类名::静态成员。
2)静态成员变量的初始化
初始化必须在类的代码文件中进行。
12. 静态成员函数
如静态成员变量一样,静态成员函数是属于类的本身,不属于任何类的对象;
如静态成员变量访问一样,可以通过:类名::静态成员来访问类的静态成员变量。
注意:静态成员函数,没有this指针;
例如:
13. 友元类和友元函数
多数时候,类和函数需要运行很紧密;但需使用显示函数来打印相关信息,这并不显得很隐藏类的细节;
这时,友元类很友元函数就很好的访问私有细节;
1)友元函数
友元函数访问类的私有成员,就如其是类的一个成员函数。
一个友元函数可以是,也可以不是其他类的成员函数;使用关键字friend。
例如:
一个友元函数可以是多个类的友元函数;
2)友元类
友元类是,声明在其他类中,可以访问其他类的私有变量;
在使用友元函数和友元类时,请务必谨慎。
14. 匿名变量和对象
所谓匿名,就是可以不通过命名变量来访问,减少临时变量。
【免责特此声明:
1)本内容可能是来自互联网的,或经过本人整理的,仅仅代表了互联网和个人的意见和看法!
2)本内容仅仅提供参考,任何参考该内容造成任何的后果,均与原创作者和本博客作者无关!】
封装
C++是一门集面向过程,面向对象以及泛型编程于一体的强大的编程语言,在这里面最重要的要属面向对象了吧???什么是面向对象?面向对象的思想是什么???总结下来就一句话:万物皆对象.在面向对象的世界里,一切都是可以用对象来解释的.这也是面向对象思想的精髓部分.
万物皆对象
举个例子:在超时里面进行商品购物的时候,它所有的商品都是按类目来划分的.生活用品,食品等.这就是按类别来分.而在自然界中也是.如:动物昆虫等.这就是类.将一类相似的的东西抽象成一类东西.
结构化程序设计
程序 = 算法 +数据结构
面向对象设计OOP
- OBJECT ORIENRED PROGRAMING
- Object oritened programing
- 程序 = 对象 +对象 +....
- 关键:让每个对象都负责执行一组相关的任务
面向对象开发范式的特性:
- 万物皆是对象
- 程序是一组对象彼此之间在发送消息
- 每个对象都有自己的内存占用,可以组装成更大的对象
- 每个对象都有类型,特定类型的对象都可以接收相同的消息
概念:
- 类:类是创建对象的模板和蓝图,类是一组相似对象的共同抽象定义
- 对象:对象是类的实例化结果,对象是实实在在的存在,代表现实世界的某一事物
对象的三大特性:
- 行为:对象能干什么
- 状态:对象的属性,行为的结果
- 标识:对象的唯一身份;
类和对象的区别:
- 类是静态定义
- 对象是动态实例
- 建立模型得到的是类而非对象
联系:
- 类是对象的定义
- 对象的产生离不开类这个模板
- 类存在的摸底是实例化得到对象
世界是由对象组成的
定义一个类的步骤:
1:定义类名
2:编写类的数据成员代表属性
3:编写类的方法代表行为
类的建模是一个抽象和封装的过程:
抽象:去掉不关注的,次要的信息而保留重要的信息
封装:信息打包
具体一点:将数据和行为结合在一个包中,对对象的使用者隐藏数据的具体实现方式
实现封装的关键:不能让类中的方法直接访问其他类的内部数据,只能通过公开行为方法间接访问:
- 例子:
-
- class ClassName{
- field1;
- field2;
- .....;
- constructor;
- .....
- method1;
- method2;
- }
对象的两种类的形式:
- 1:结构体形式:
- struct Saving{
- unsigned accountNumber;
- float balance;
- };
- 缺点:安全性不好,任何人都是可以进行访问的
-
- 2:class形式:
- class Savings{
- public:
- float deposit(float amount){
- balance +=amount;
- return balance;
- }
- private:
- unsigned accountNumber;
- float balance;
- };
- 优点:类不仅可以保护数据,而且可以提供成员函数来操作
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
C++用类来定义抽象数据类型
C++早期的版本被成为带类的C
- class 类名称{
- public:
- //共有函数
- protected:
- //保护成员
- private:
- //私有函数
- //私有成员
- int val;
- };
类中定义成员函数:
- class Tdate{
- public:
- void set(int m = 1,int d = 2,int y = 3){
- month = m;
- day = d;
- year = y;
- }
- bool isLeepYear(){
- return (year%4 ==0 &&year%100!=0) || (year %400==0);
- }
- void print(){
- cout<<year<<"/"<<month<<"/"<<day<<endl;
- }
- private:
- int month,day,year;
- }
-
- 调用:
- int main(){
- Tdate d;
- d.set(2,4,1998);
- d.print();
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
在类中定义成员函数:
类中定义的成员函数一般都为内联函数,即使没有明确用inline标示
在C++中,类定义通常在头文件中,因此这些成员函数定义也伴随这进入头文件
在类之后定义成员函数:
C++允许在其他地方定义成员函数;
将类定义和其成员函数定义分开,是目前开发程序的通常做法
我们把类定义看成是类的外部接口,类的成员函数定义看成是类的内部实现
看一个最简单的实例:在main.cpp中去定义一个类.并且在此类中直接实现成员函数.(也可以通过Car.h和Car.cpp的形式去实现)
- /*
- * ===========================================================================
- *
- * Filename: main.cpp
- * Description:
- * Version: 1.0
- * Created: 2017年05月26日 22时24分05秒
- * Revision: none
- * Compiler: gcc
- * Author: (),
- * Company:
- *
- * ===========================================================================
- */
-
- #include<iostream>
- #include<stdio.h>
- #include<stdlib.h>
- using namespace::std;
-
- class Car{
- public:
- void run(){
- cout <<"car run"<<endl;
- }
-
- void shut(){
- cout<<"car shutdown"<<endl;
- }
-
- void setProperty(int price,int carNum){
- this->mPrice = price;
- this->mCarNum = carNum;
- }
-
- private:
- int mPrice;
- int mCarNum;
- };
-
- int main(int argc,char *argv[]){
- Car mCar;
- /* *类的成员函数是不会占用内存的 */
- cout <<"sizeof Car" <<sizeof(mCar)<<endl;
- cout << &mCar <<endl;
- mCar.setProperty(10000,1000000);
- mCar.run();
- mCar.shut();
-
- return 0;
- }
通过Car.cpp及Car.h来实现一个简单类的封装
- /*
- * ===========================================================================
- *
- * Filename: Car.h
- * Description:
- * Version: 1.0
- * Created: 2017年05月26日 22时43分53秒
- * Revision: none
- * Compiler: gcc
- * Author: (),
- * Company:
- *
- * ===========================================================================
- */
-
- #ifndef __CAR_H__
- #define __CAR_H__
-
- #ifdef __cplusplus
- extern "C"{
- #endif
-
- /* *
- * 在头文件中去定义一个Car类型
- * */
- class Car{
- private:
- int mCarPrice;
- int mCarNum;
- int mCarType;
- public:
- void run();
- void shut();
- void setProperty(int price,int carNum,int carType);
- void print();
- };
-
- #ifdef __cplusplus
- }
- #endif
-
- #endif
- /*
- * ===========================================================================
- *
- * Filename: Car.cpp
- *
- * Version: 1.0
- * Created: 2017年05月26日 22时48分41秒
- * Revision: none
- * Compiler: gcc
- * Author: (),
- * Company:
- *
- * ===========================================================================
- */
-
- #include<iostream>
- using namespace::std;
-
- /**
- * 包含Car头文件
- */
- #include<Car.h>
-
- /* *
- *类的行为的实现,定义
- * */
- void Car::run(){
- cout << "car run" << endl;
- }
-
- void Car::shut(){
- cout << "car shut" << endl;
- }
-
- void Car::setProperty(int price,int carnum,int cartype){
- mCarPrice = price;
- mCarNum = carnum;
- mCarType = cartype;
- if(mCarType == 1){
- cout << "car type is one" <<endl;
- }else if(mCarType == 2){
- cout << "car type is two" <<endl;
- }else if(mCarType == 3){
- cout << "car type is three" <<endl;
- }
- }
-
- void Car::print(){
- cout <<"price:"<<mCarPrice<<"\n";
- cout <<"carnum:"<<mCarNum<<"\n";
- cout <<"carType"<<mCarType<<endl;
- }
- /*
- * ===========================================================================
- *
- * Filename: CarTest.cpp
- * Description:
- * Version: 1.0
- * Created: 2017年05月26日 23时18分01秒
- * Revision: none
- * Compiler: gcc
- * Author: (),
- * Company:
- *
- * ===========================================================================
- */
-
- #include<iostream>
- using namespace::std;
-
- #include<Car.h>
- /* *
- *使用指针的方式来调用成员函数,类似结构体指针调用成员变量
- * */
- void usePoint(Car *mCar){
- cout<<"==============="<<endl;
- mCar ->setProperty(20000,20002,3);
- mCar ->run();
- mCar ->shut();
- mCar ->print();
- }
-
-
- /* *
- *使用引用的形式来调用成员函数,与对象的调用一致
- * */
- void userReference(Car &mCar){
- cout << "==============="<<endl;
- mCar.setProperty(10000,100001,2);
- mCar.run();
- mCar.shut();
- mCar.print();
- }
-
-
- int main(int argc,char *argv[]){
- Car mCar;
- cout << "address:"<< &mCar <<endl;
- cout <<"size of mCar"<< sizeof(mCar) <<endl;
- mCar.setProperty(80001,88888,1);
- mCar.run();
- mCar.shut();
-
- usePoint(&mCar);
- userReference(mCar);
-
- return 0;
- }
以上是简单的关于类和对象以及一个简单的封装的国政
在成员函数中访问成员:
- 成员函数必须用对象来调用
- Car mCar;
- mCar.run();
- 在成员函数内部,访问数据成员或成员函数无需如此
- void setProperty(int price,int carNum){
- this->mPrice = price;
- this->mCarNum = carNum;
- }
this指针代表当前对象占用内存空间的地址:
- void Tdate::set(int m ,int d,int y){
- this->month = m;
- this->day = d;
- this->year = y;
- }
通过指针来调用成员函数:
- 例子:
- #include"tdate.h"
- #include<iostream>
- void func(Tdate *pDate){
- pDate->print();
- if(pDate->isLeepYear()){
- cout<<"leepyear"<<endl;
- }else{
- cout<<"not leepyear"<<endl;
- }
- }
通过引用来调用成员函数:
- #include"tdate.h"
- #include<iostream>
- void func(Tdate &pDate){
- pDate.print();
- if(pDate.isLeepYear()){
- cout<<"leepyear"<<endl;
- }else{
- cout<<"not leepyear"<<endl;
- }
- }
类的成员函数的重载:
类的成员函数可以像普通函数一样进行重载
但是不同的类即使有相同的函数名也不算重载
类的成员函数可以默认设置成员参数:
在类之外去定义这样一个类的行为
OOP三大特性(面向对象的三个特性):
继承(inheritance):
多态(polymorphism):
封装(encapsulation):类背后隐藏的思想是数据抽象和封装
信息隐藏,隐藏对象的实现细节,不让外部直接访问
将数据成员和成员函数一起包装到 一个单元中 ,单元以类的形式实现
将数据成员和成员函数包装进类中,加上具体实现的隐藏,共同被称作封装,其结果是一个同时带有特征和行为的数据类型
封装类:定义类,定义其成员函数的过程称为封装类
细节:
- 除非必须公开底层的实现细节,否则应该将所有字段指定为private
- 使数据成员私有,控制数据访问限制.增强了类的可维护性
- 隐藏方法的具体实现.向外部提供公开的接口.以供安全调用
信息隐藏是OOP最重要的特性之一,也是可以使用访问修饰符的原因
访问修饰符号:
- public
- protected
- private
信息隐藏的原因:
对模块的任何实现细节所做的更改不会影响使用该模块的代码
防止用户意外的修改数据
使模块易于使用和维护
这段时间看了不少C++代码,也写了一个小项目,这篇文章来说一下我见到过的比较通用的两种多线程封装方式,实现平台为Linux
首先说说地一种线程封装方式,也是我们平常见得最多的一种封装方式,是用面向对象中的继承,多态来实现的,下面来看具体的代码,这些代码使我随手写的,主要是为了说明思想,如果要用到项目中还需要完善。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。