C++中级基础(上)

C++类 & 对象

C++在C语言的基础上增加面向对象编程,也就是说C++支持面向对象程序设计(Java python都是支持面向对象程序设计的)。类是C++的核心特性,通常被称为用户定义的类型。
类定义以关键字class开头,后跟类的名称。类的主体是包含在一对花括号中,类定义后必须跟着一个分号或一个声明列表。关键字public确定类成员的访问属性,在类对象作用域内,公共成员在类的外面是可以访问的。对象是根据类来创建的,声明类的对象就像声明基本类型的变量一样。

class Box
{
   public:
      double length;   // 盒子的长度
      double breadth;  // 盒子的宽度
      double height;   // 盒子的高度
};
Box Box1;          // 声明 Box1,类型为 Box

举例:

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
};
 
int main( )
{
   Box Box1;        // 声明 Box1,类型为 Box
   Box Box2;        // 声明 Box2,类型为 Box
   double volume = 0.0;     // 用于存储体积
 
   // box 1 详述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;
 
   // box 2 详述
   Box2.height = 10.0;
   Box2.length = 12.0;
   Box2.breadth = 13.0;
 
   // box 1 的体积
   volume = Box1.height * Box1.length * Box1.breadth;
   cout << "Box1 的体积:" << volume <<endl;
 
   // box 2 的体积
   volume = Box2.height * Box2.length * Box2.breadth;
   cout << "Box2 的体积:" << volume <<endl;
   return 0;
}

C++类成员函数
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
使用成员函数来访问类的成员,而不是直接访问这些类的成员:

class Box
{
   public:
      double length;         // 长度
      double breadth;        // 宽度
      double height;         // 高度
      double getVolume(void);// 返回体积
};

定义在类中的成员函数缺省都是内联的,如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上inline并使用范围解析运算符::定义该函数,否则就认为不是内联的。需要强调的是在::运算符之前必须使用类名。

// 头文件
class A
{
    public:
    void Foo(int x, int y);
}
// 定义文件
inline void A::Foo(int x, int y){}

调用成员函数是在对象上使用点运算符(.)

Box myBox;          // 创建一个对象
 
myBox.getVolume();  // 调用该对象的成员函数

C++类访问修饰符
关键字public,private,protected称为访问修饰符
public 公有成员在程序中类的外部是可访问的,在不使用任何成员函数来设置和获取公有变量的值。
private 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。在没有使用任何访问修饰符时,类的成员将被假定为私有成员
protected 保护成员在派生类(即子类)中是可访问的。
继承中public,protected,private的继承方式:

image.png

C++ 类构造函数 & 析构函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。构造函数的名称与类的名称是完全相同的,并不会返回任何类型,也不会返回void,可用于为某些成员变量设置初始值。

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();  // 这是构造函数
 
   private:
      double length;
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line; //类的新对象line,此时执行构造函数
 
   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

输出

Object is being created
Length of line : 6

同理,带参数的构造函数在创建对象时会给对象赋初始值。

使用初始化列表来初始化字段
Line::Line( double len): length(len)
{
    cout << "Object is being created, length = " << len << endl;
}
等价于
Line::Line( double len)
{
    length = len;
    cout << "Object is being created, length = " << len << endl;
}
类的析构函数

类的析构函数是类的一种特殊的成员函数,会每次删除所创建的对象时执行。析构函数的名称与类的名称是完全相同的,只是在前面加波浪号(~)作为前缀,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件,释放内存等)前释放资源。

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();   // 这是构造函数声明
      ~Line();  // 这是析构函数声明
 
   private:
      double length;
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
Line::~Line(void)
{
    cout << "Object is being deleted" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return 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

强调一点 C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
1,通过使用另一个同类型的对象来初始化新创建的对象
2,复制对象把它作为参数传递给函数
3,复制对象,并从函数返回这个对象

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      int getLength( void );
      Line( int len );             // 简单的构造函数
      Line( const Line &obj);      // 拷贝构造函数
      ~Line();                     // 析构函数
 
   private:
      int *ptr;
};
 
// 成员函数定义,包括构造函数
Line::Line(int len)
{
    cout << "调用构造函数" << endl;
    // 为指针分配内存
    ptr = new int;
    *ptr = len;
}
 //拷贝构造函数 obj是一个对象引用,该对象用于初始化另一个对象
Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}
 
Line::~Line(void)
{
    cout << "释放内存" << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}
 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}
 
// 程序的主函数
int main( )
{
   Line line(10);
 
   display(line);
 
   return 0;
}

输出:

调用构造函数
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
释放内存

C++友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员,若友元是一个函数,则该函数被称为友元函数;若友元是一个类,该类被称为友元类这时,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字friend

class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );
   void setWidth( double wid );
};

声明类ClassTwo的所有成员函数作为类ClassOne的友元,需要在类ClassOne的定义中放置如下声明:

friend class ClassTwo;

C++ this 指针
在C++中,每一个对象都能通过this指针来访问自己的地址,this指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。友元函数没有this指针,因为友元不是类的成员,只有成员函数才有this指针
C++类的静态成员
使用static关键字来把类成员定义为静态的,当我们声明类的成员是静态时,意味着无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是共享的,静态成员的初始化是在类的外部通过使用范围解析运算符::来重新声明静态变量从而对它进行初始化。静态成员函数的用法是一样的。

#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;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};
 
// 初始化类 Box 的静态成员
int Box::objectCount = 0;
 
int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // 声明 box1
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2
 
   // 输出对象的总数
   cout << "Total objects: " << Box::objectCount << endl;
 
   return 0;
}

C++继承

继承允许依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易,也达到重用代码功能和提高执行时间的效果。当创建一个类时不需要重新编写新的数据成员和成员函数,只需指定新建的类继承已有的类成员即可。这个已有的类称为基类,新建的类称为派生类

class derived-class: access-specifier base-class

访问修饰符 access-specifier是public protected或private其中的一个,base-class是之前定义过的某个类的名称。如果未使用访问修饰符access-specifier,则默认为private
一个派生类继承了所有基类方法,但下列情况除外:
基类的构造函数,析构函数和拷贝构造函数
基类的重载运算符
基类的友元函数

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

C++允许在同一个作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载
C++中的函数重载 在同一个作用域中,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数,类型或顺序)必须不同。
C++中的运算符重载其中重载运算符是带有特殊名称的函数,函数名由关键字operator和其后要重载的运算符符号构成(这部分不太容易理解,小白我就了解了解就好)

Box operator+(const Box&);

C++多态

多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。虚函数在基类中使用关键字virtual声明的函数,在派生类中重新定义基类中的虚函数,会告诉编译器不要静态链接到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接或后期绑定
虚函数可以不实现(定义),不实现(定义)的虚函数是纯虚函数,其声明如下:

virtual void funtion1()=0;

C++通过创建类来支持封装和数据隐藏,
数据封装是一种数据和操作数据的函数捆绑在一起的机制
数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制
抽象类(ABC)不能被用于实例化对象,只能作为接口使用。因此如果一个抽象类的子类需要被实例化则必须实现每个虚函数也就意味着C++支持使用ABC声明接口。如果没有在派生类中重写纯虚函数就尝试实例化该类的对象,会导致编译错误。

#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

感言以前只学基础的时候,感觉不到这个深度。发现有些地方还是不是太理解,可能是还没练手的缘故吧

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容