C++——类

本文仅作为个人学习笔记,文档内容和实例绝大部分摘自菜鸟教程,读者觉得本文有帮助请关注菜鸟教程官网:https://www.runoob.com/

什么是类

定义:  
 类(class)提供的自定义数据类型的机制。类可以包含数据、函数和类型成员。一个类定义一种新的类型和一个新的作用域。

释义:
进入工业4.0以来,优秀的企业开始用智能化生产线替代传统的人工作业。假设我们是某车企智能化产线的程序设计者,我们应做如下工作:

  • 了解产线工序,假设该条产线焊接、冲压、涂装。
  • 为每一个环节定义属性和需要执行的操作,例如涂装环节,需要定义喷涂的位置、喷涂量等属性并定义喷涂方式等操作方法。

在设计过程中,我们会发现在某一个环节中,通常由多台设备共同完成该环节工作, 即需要在程序中对每一个设备程序添加添加相同属性类型和方法,这种设计方式使我们的程序变得庞大且增加了开发人员的工作量。为了避免这种重复的工作,设计人员将某一环节具有相同属性类型和方法的对象抽象为一种类型,该类型的对象具有相同的属性和方法,初始化该类型的时候软件会自动将类型属性和方法赋予对于该类型的不同对象仅需要对差异属性或方法进行修改,这大大提高了开发效率。这种抽象出来的类型就是我们本节的核心,类。

类由什么组成?

类成员:
类成员可以是数据、函数、类型别名

如何定义类

该图引用语菜鸟教程
类定义是以关键字class + 自定义的类名称作为开头,函数名后的一对花括号为类的主题部分,右花括号后跟一个分号作为结尾。如下所示:

class Box  			   //类名
{
   public:			   // 访问修饰符
      double length;   // 盒子的长度
      double breadth;  // 盒子的宽度
      double height;   // 盒子的高度
};

如何定义对象

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

Box Box1;          // 声明 Box1,类型为 Box
Box Box2;          // 声明 Box2,类型为 Box

如何访问成员数据

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

该图引用语菜鸟教程
为了更好地理解这些概念,让我们尝试一下下面的实例:

#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;     // 用于存储体积
 
   // 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;
 
 
   // box 3 详述
   Box3.set(16.0, 8.0, 12.0); 
   volume = Box3.get(); 
   cout << "Box3 的体积:" << volume <<endl;
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Box1 的体积:210
Box2 的体积:1560
Box3 的体积:1536

什么是成员函数

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

让我们看看之前定义的类 Box,现在我们要使用成员函数来访问类的成员,而不是直接访问这些类的成员

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

double Box::setVolume(double l, double b, double h) {
    length = l;
    breadth = b;
    height = h;
}

成员函数可以定义在类定义内部,如上例中getVolume函数,在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。

或者在类的外部单用范围解析运算符 :: 来定义,如上例中setVolume函数,在 :: 运算符之前必须使用类名

:: 叫作用域区分符,指明一个函数属于哪个类或一个数据属于哪个类。
:: 可以不跟类名,表示全局数据或全局函数(即非成员函数)。

double length;   // 全局变量
double breadth;  // 全局变量
double height;   // 全局变量
void setVolume(double l, double b, double h) { //全局函数
    length = l;
    breadth = b;
    height = h;
}
class Box
{
public:
    double length;   // 长度
    double breadth;  // 宽度
    double height;   // 高度
    double setVolume(double l, double b, double h){  //成员函数
        ::setVolume(l, b, h); //非成员函数
    }
};

int main( )
{
   Box box; 
   box.setVolume(2, 3, 4);
   cout << "Box height:" << box.height << " 全局变量 height:" << height << endl;
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Box height:6.47087e-314 全局变量 height:4

因为程序中成员函数调用了全局函数setVolume,所以全局变量height被赋值为4。成员变量未被赋值,所以为随机数。

调用成员函数是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据,如下所示:

int main( )
{
   Box box;
   box.setVolume(2, 3, 4); //使用点运算符(.)调用成员函数
   cout << "Box 的体积:" << box.getVolume() << endl;
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Box 的体积:24

访问修饰符有什么作用

访问修饰符:public、private、protected。

数据封装是面向对象编程的重要特点,它防止函数直接访问类内成员。
类成员的访问限制是通过在类主体内部对各个区域标记public、private、project来指定的。

一个类可以有多个public、protected或private标记区域。
每个标记区域在下一个标记区域开始之前或者在遇到类主体结束右括号之前都是有效的。
成员或类的默认访问修饰符是private。

class Box
{
    double volume;
public:              //共有成员
    double length;
public:         	 //共有成员
    double breadth;  
private:         	 //私有成员
    double height;   
    double setVolume(double l, double b, double h){  //成员函数
        length = l;
        breadth = b;
        height = h;
    }
protected:         //受保护成员
};

int main( )
{
   Box box;
   cout << "Box volume:" << box.volume << endl;
   return 0;
}

运行结果如下,由于成员变量前未生命访问修饰符,故声明为默认访问修饰符private。

error: ‘volume’ is a private member of ‘Box’

公有(public)成员

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

#include <iostream>
 
using namespace std;
 class Line
{
   public:
      double length;
      void setLength( double len );
      double getLength( void );
};
 
// 成员函数定义
double Line::getLength(void)
{
    return length ;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
// 程序的主函数
int main( )
{
   Line line;
 
   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   // 不使用成员函数设置长度
   line.length = 10.0; // OK: 因为 length 是公有的
   cout << "Length of line : " << line.length <<endl;
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Length of line : 6
Length of line : 10

私有(private)成员

私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。
只有类和友元函数可以访问私有成员。
默认情况下,类的所有成员都是私有的。例如在下面的类中,width 是一个私有成员,这意味着,如果您没有使用任何访问修饰符,类的成员将被假定为私有成员:

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;
      void setWidth( double wid );
      double getWidth( void );
 
   private:
      double width;
};
 
// 成员函数定义
double Box::getWidth(void)
{
    return width ;
}
 
void Box::setWidth( double wid )
{
    width = wid;
}
 
// 程序的主函数
int main( )
{
   Box box;
 
   // 不使用成员函数设置长度
   box.length = 10.0; // OK: 因为 length 是公有的
   cout << "Length of box : " << box.length <<endl;
 
   // 不使用成员函数设置宽度
   // box.width = 10.0; // Error: 因为 width 是私有的
   box.setWidth(10.0);  // 使用成员函数设置宽度
   cout << "Width of box : " << box.getWidth() <<endl;
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Length of box : 10
Width of box : 10

protected(受保护)成员

protected(受保护)成员变量或函数与私有成员十分相似,但有一点不同,protected(受保护)成员在派生类(即子类)中是可访问的。

在学习派生类和继承的知识之前,先看一个简单示例,我们从父类 Box 派生了一个子类 smallBox。

下面的实例与前面的实例类似,在这里 width 成员可被派生类 smallBox 的任何成员函数访问。

#include <iostream>
using namespace std;

class Box       //基类        
{
protected:      //受保护成员
    double width;
};

class SmallBox:Box // SmallBox 是派生类
{
public:
    void setSmallWidth( double wid );
    double getSmallWidth( void );
};

// 子类的成员函数
double SmallBox::getSmallWidth(void)
{
    return width ; //子类未生命width,但可以访问父类的受保护成员 width
}

void SmallBox::setSmallWidth( double wid )
{
    width = wid; //子类未生命width,但可以修改父类的受保护成员 width
}

// 程序的主函数
int main( )
{
    SmallBox box;

    // 使用成员函数设置宽度
    box.setSmallWidth(5.0);
    cout << "Width of box : "<< box.getSmallWidth() << endl;

    return 0;
}

继承中的特点

有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
  4. 默认继承:继承时不显示声明是private,protected,public继承,则默认是private继承。

1.private成员只能被本类成员(类内)和友元访问,不能被派生类访问;
2.protected成员可以被派生类访问;
3.在struct中默认public继承。

public继承示例

#include<iostream>
#include<assert.h>
using namespace std;

class A{
public:
    int a;
    A(){
        a1 = 1;
        a2 = 2;
        a3 = 3;
        a = 4;
    }
    void fun(){
        cout << a << endl;    //正确  成员函数可以访问private成员变量
        cout << a1 << endl;   //正确  成员函数可以访问public成员变量
        cout << a2 << endl;   //正确  成员函数可以访问protected成员变量
        cout << a3 << endl;   //正确  成员函数可以访问private成员变量
    }
public:
    int a1;
protected:
    int a2;
private:
    int a3;
};
class B : public A{  //B类继承于public A,A类是B类的父类
public:
    int a;
    B(int i){
        A();
        a = i;
    }
    void fun(){
        cout << a << endl;       //正确,public成员
        cout << a1 << endl;       //正确,基类的public成员,在派生类中仍是public成员。
        cout << a2 << endl;       //正确,基类的protected成员,在派生类中仍是protected可以被派生类访问。
        cout << a3 << endl;       //错误,基类的private成员不能被派生类访问。
    }
};

int main(){
    B b(10);
    cout << b.a << endl;
    cout << b.a1 << endl;   //正确
    cout << b.a2 << endl;   //错误,类外不能访问protected成员
    cout << b.a3 << endl;   //错误,类外不能访问private成员
    system("pause");
    return 0;
}

protected示例继承

#include<iostream>
#include<assert.h>
using namespace std;

class A{
public:
    int a;
    A(){
        a1 = 1;
        a2 = 2;
        a3 = 3;
        a = 4;
    }
    void fun(){
        cout << a << endl;    //正确  成员函数可以访问private成员变量
        cout << a1 << endl;   //正确  成员函数可以访问public成员变量
        cout << a2 << endl;   //正确  成员函数可以访问protected成员变量
        cout << a3 << endl;   //正确  成员函数可以访问private成员变量
    }
public:
    int a1;
protected:
    int a2;
private:
    int a3;
};
class B : protected A{
public:
    int a;
    B(int i){
        A();
        a = i;
    }
    void fun(){
        cout << a << endl;       //正确,public成员。
        cout << a1 << endl;       //正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。
        cout << a2 << endl;       //正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。
        cout << a3 << endl;       //错误,基类的private成员不能被派生类访问。
    }
};
int main(){
    B b(10);
    cout << b.a << endl;       //正确。public成员
    cout << b.a1 << endl;      //错误,protected成员不能在类外访问。
    cout << b.a2 << endl;      //错误,protected成员不能在类外访问。
    cout << b.a3 << endl;      //错误,private成员不能在类外访问。
    system("pause");
    return 0;
}

private继承示例

#include<iostream>
#include<assert.h>
using namespace std;

class A{
public:
    int a;
    A(){
        a1 = 1;
        a2 = 2;
        a3 = 3;
        a = 4;
    }
    void fun(){
        cout << a << endl;    //正确  成员函数可以访问private成员变量
        cout << a1 << endl;   //正确  成员函数可以访问public成员变量
        cout << a2 << endl;   //正确  成员函数可以访问protected成员变量
        cout << a3 << endl;   //正确  成员函数可以访问private成员变量
    }
public:
    int a1;
protected:
    int a2;
private:
    int a3;
};
class B : private A{
public:
    int a;
    B(int i){
        A();
        a = i;
    }
    void fun(){
        cout << a << endl;       //正确,public成员。
        cout << a1 << endl;       //正确,基类public成员,在派生类中变成了private,可以被派生类访问。
        cout << a2 << endl;       //正确,基类的protected成员,在派生类中变成了private,可以被派生类访问。
        cout << a3 << endl;       //错误,基类的private成员不能被派生类访问。
    }
};
int main(){
    B b(10);
    cout << b.a << endl;       //正确。public成员
    cout << b.a1 << endl;      //错误,private成员不能在类外访问。
    cout << b.a2 << endl;      //错误, private成员不能在类外访问。
    cout << b.a3 << endl;      //错误,private成员不能在类外访问。
    system("pause");
    return 0;
}

成员函数

c++的类有六个默认成员函数:

  1. 构造函数
  2. 拷贝构造函数
  3. 析构函数
  4. 赋值操作符重载
  5. 取地址操作符重载
  6. const修饰的取地址操作符重载

即使程序设计人员未声明默认成员函数,编译器仍然会声明对应默认的成员函数。

构造函数

  1. 构造函数会在每次创建类的新对象时执行。
  2. 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回void。
  3. 构造函数可用于某些成员变量设置初始值。
    下面的实例有助于更好地理解构造函数的概念:
#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.setLength(6.0);
    cout << "Length of line : " << line.getLength() <<endl;

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Object is being created
Length of line : 6

  1. 默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。
#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 << "Object is being created, length = " << len << endl;
    length = len;
}

void Line::setLength( double len )
{
    length = len;
}

double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
    Line line(10.0);

    // 获取默认设置的长度
    cout << "Length of line : " << line.getLength() <<endl;
    // 再次设置长度
    line.setLength(6.0);
    cout << "Length of line : " << line.getLength() <<endl;

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Object is being created, length = 10
Length of line : 10
Length of line : 6

  1. 构造函数支持使用初始化列表来初始化字段
    与其他函数不同,构造函数除了名字,参数列表和函数体之外,还可以在定义处有初始化列表,初始化列表以冒号开头,后跟一些列以逗号分隔的初始化字段。
Box::Box(double l, double w, double, h):length(h), width(w), height(h)
{
}

上面的语法等同于如下语法:

Box::Box(double l, double w, double, h)
{
	length = h;
	width = w;
	height = h;
}

C++ 初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
初始化顺序最好要按照变量在类声明的顺序一致
class CMyClass {
CMyClass(int x, int y);
int m_x;
int m_y;
};
CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y)
{
};
你可能以为上面的代码将会首先做 m_y=y,然后做 m_x=m_y,最后它们有相同的值。
但是编译器先初始化 m_x,然后是 m_y,因为它们是按这样的顺序声明的。结果是 m_x 将有一个不可预测的值。

析构函数

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

下面的实例有助于更好地理解析构函数的概念:

#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

拷贝构造函数

  1. 拷贝构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。
classname (const classname &obj) {
   // 构造函数的主体
}
  1. 它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
  2. 拷贝构造函数被执行的三种情况:
    • 一个对象以值传递的方式传入函数体,隐式调用。
    • 一个对象以值传递的方式从函数返回,隐式调用。
    • 一个对象需要通过另外一个对象进行初始化,显示调用。

    函数参数为 对象的引用时,不会调用拷贝构造函数。
    以值传递会创建临时对象,引用不会。

#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 << "调用构造函数, 对象地址:" << this << endl;
    // 为指针分配内存
    ptr = new int;
    *ptr = len;
}

Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存, 对象地址:" << this << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}

Line::~Line(void)
{
    cout << "释放内存, 对象地址:" << this << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}

void display(Line obj)
{
    cout << "line 大小 : " << obj.getLength() <<endl;
}

// 程序的主函数
int main( )
{
    Line line1(10);

    Line line2 = line1; // 这里也调用了拷贝构造函数

    display(line1);
    display(line2);

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

//构造line1
调用构造函数, 对象地址:0x3087429f0
//拷贝构造line2
调用拷贝构造函数并为指针 ptr 分配内存, 对象地址:0x3087429e8
//display1 传参调用拷贝构造
调用拷贝构造函数并为指针 ptr 分配内存, 对象地址:0x3087429d0
line 大小 : 10
//display1 释放line1的拷贝对象析构函数
释放内存, 对象地址:0x3087429d0
//display2 传参调用拷贝构造
调用拷贝构造函数并为指针 ptr 分配内存, 对象地址:0x3087429c8
line 大小 : 10
//display2 释放line2的拷贝对象析构函数
释放内存, 对象地址:0x3087429c8
//line2 析构
释放内存, 对象地址:0x3087429e8
//line1 析构
释放内存, 对象地址:0x3087429f0

  1. 当类成员中含有指针类型成员且需要对其分配内存时,一定要有总定义拷贝构造函数。
    当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。所以,这时,必须采用深拷贝。
    深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。
  2. 如何防止默认拷贝发生?
    声明一个私有的拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类的对象,编译器会报告错误,从而可以避免按值传递或返回对象。

函数

友元函数
  1. 类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。
  2. 尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
  3. 友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
  4. 如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend
#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)
    {
        // BigBox是Box的友元类,它可以直接访问Box类的任何成员
        box.setWidth(width);
        cout << "Width of box : " << box.width << endl;
    }
};

// 成员函数定义
void Box::setWidth(double wid)
{
    width = wid;
}

// 请注意:printWidth() 不是任何类的成员函数
void printWidth(Box box)
{
    /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
    cout << "Width of box : " << box.width << endl;
}

// 程序的主函数
int main()
{
    Box box;
    BigBox big;

    // 使用成员函数设置宽度
    box.setWidth(10.0);

    // 使用友元函数输出宽度
    printWidth(box);

    // 使用友元类中的方法设置宽度
    big.Print(20, box);

    getchar();
    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Width of box : 10
Width of box : 20

内联函数
  1. C++ 内联函数是通常与类一起使用。
  2. 如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。
#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

优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联.

缺点: 滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。

引入内联函数的目的是为了解决程序中函数调用的效率问题。
程序在编译器编译的时候,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体进行替换,而对于其他的函数,都是在运行时候才被替代。这其实就是个空间代价换时间的节省。所以内联函数一般都是1-5行的小函数。在使用内联函数时要留神:
1.在内联函数内不允许使用循环语句和开关语句;
2.内联函数的定义必须出现在内联函数第一次调用之前;
3.类结构中所在的类说明内部定义的函数是内联函数。
4.只有当函数只有 10 行甚至更少时才将其定义为内联函数.
5.有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数.

this指针

在C++中,每一个对象都能通过this指针来访问自己的地址。this指针是所有成员函数的隐含参数。因此,在成员函数内部,他可以用来指向调用对象。
**友元函数没有this指针,因为友元不是类的成员。只有成员函数才有this指针。

#include <iostream>
using namespace std;

class Box{
public:
    Box(){;}
    ~Box(){;}
    Box* get_address()   //得到this的地址
    {
        return this;
    }
};

int main(){

    Box box1;
    Box box2;
    // Box* 定义指针p接受对象box的get_address()成员函数的返回值,并打印

    Box* p = box1.get_address();
    cout << p << " " << (p == &box1) << endl; //this == box1对象的地址

    p = box2.get_address();
    cout << p << " " << (p == &box2) << endl;

    return 0;
}

todo
构造函数里面定义和初始化列表定义有啥区别?
定义是在类本身定义的时候进行的(就是你class的那个大括号里进行的),
调用构造函数的时候,首先是给该实例(类的具体对象)分配空间,
使用初始化列表的话,就在分配空间的时候,同时将其空间初始化,
但在构造函数的大括号里,所有变(常)量的空间都已经分配好了,
而常量本身是不能改变值的,这时对其调用的“=”只能是赋值,
也就是改变其现有值,当然是非法操作。

难道在构造函数里会重新定义初始化列表定义的变量吗?
定义是在类本身定义的时候进行的,跟初始化列表和构造函数都没有关系。

或者,不管生成多少类的对象,初始化列表的定义仅仅只被执行一次,而构造函数内部却被执行很多次?
在每个类对象被生成的时候,都仅仅会执行一次构造函数,初始化列表(存在的话)也仅仅会被执行一次。

或者,进入构造函数的括号里时,所有成员变量已经被初始化了?
执行到构造函数的大括号里的时候,所有成员的空间都已经分配好了,
无论是否已经被初始化过。

建议LZ好好了解一下:定义、声明、初始化、赋值这几个概念,说白了:
声明:就是告诉编译器,这个变量名是有定义的(不一定在这里定义,但编译器应该能找到定义)
定义:给一个变量分配空间的地方,根据规定,在整个编译过程里,每个变量有且仅能有一个定义(只有极少数例外)
初始化:在变(常)量被定义的同时,在被分配的空间里,放入的值,这个概念对常量比较明显,因为常量只能被初始化。
赋值:在变(常)量的空间被分配好了以后,再修改该空间内容的操作。

不知道这么说,LZ明白了没有,其实,把大家常推荐的那些C++经典入门书随便哪本好好看看,都能把这些问题搞明白。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值