C++ 类 & 对象

2023-11-13

        C++ 在 C 的基础上增加了面向对象编程(OOP),支持面向对象程序设计。类是 C++ 的核心特性,一种用户自定义的类型,用于指定对象的形式。类包含数据和用于处理数据的方法(函数),数据称为成员变量。函数称为成员函数。类可以看作是一种模板,用来创建具有相同属性和行为的多个对象。

类定义

        定义一个类,本质上是定义一个数据类型的蓝图,实际并未定义任何数据,而是定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。
        类定义是以关键字 class 开头,后跟类的名称。类的主体是包含在一对花括号中。类定义后必须跟着一个分号或一个声明列表。例如,使用关键字 class 定义 Box 数据类型,如下所示:

class Box
{
public:
    double length; 
    double breadth;
    double height; 
};

        关键字 public 确定类成员的访问属性。在类对象的作用域内,公共成员在类的外部是可访问的。也可以指定类的成员为 private 或 protected。

定义对象

        类提供了对象的蓝图,所以基本上对象是根据类来创建的。声明类的对象,就像声明基本类型的变量一样。下面语句声明了类 Box 的两个对象:

Box box1;  // 声明 box1,类型为 Box
Box box2;  // 声明 box2,类型为 Box

        对象 box1 和 box2 都有其各自的数据成员。

访问数据成员

        类的对象的公共数据成员可以使用直接成员访问运算符 (.) 来访问。先看一个实例:

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

	volume = box1.height * box1.length * box1.breadth;	// box 1 的体积
	cout << "The valume of box1 is " << volume << endl;

	volume = box2.height * box2.length * box2.breadth;	// box 1 的体积
	cout << "The valume of box2 is " << volume << endl;

	return 0;
}

         编译和执行上面的代码:

The valume of box1 is 210
The valume of box2 is 1560

         注意:私有的成员(private)和受保护的成员(protected)不能使用直接成员访问运算符 (.) 来直接访问。

类成员函数

         类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。通常使用成员函数来访问类的成员,而不是直接访问这些类的成员:

class Box
{
public:
    double length;           // 长度
    double breadth;          // 宽度
    double height;           // 高度
    double getvolume(void);  // 类成员函数
};

         成员函数可以定义在类定义内部,或单独使用范围解析运算符 :: 来定义。

         注意:即便没有使用 inline 标识符,在类定义中定义的成员函数依旧把函数声明为内联。所以可按照下面的方式定义 getvolume() 函数:

class Box
{
public:
    double length;      
    double breadth;     
    double height;      

    double getVolume(void)
    {
        return length * breadth * height;
    }
};

         也可以在类的外部使用范围解析运算符 :: 定义该函数,如下所示:

double Box::getVolume(void)
{
    return length * breadth * height;
}

         注意:在 :: 运算符之前必须使用类名(Box)。在对象上使用点运算符(.)来调用成员函数,这样便能操作与该对象相关的数据,如下所示:

Box myBox;          // 创建一个对象
myBox.getVolume();  // 调用该对象的成员函数
实例
#include <iostream>

using namespace std;

class Box
{
public:
    double length;   // 长
    double breadth;  // 宽
    double height;   // 高

    /*成员函数声明*/ 
    double getVolume(void);
    void setLength(double len);
    void setBreadth(double bre);
    void setHeight(double hei);
};

/*成员函数定义*/ 
double Box::getVolume(void)
{
    return length * breadth * height;
}

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

void Box::setBreadth(double bre)
{
    breadth = bre;
}

void Box::setHeight(double hei)
{
    height = hei;
}

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

    // box 1
    Box1.setLength(6.0);     
    Box1.setBreadth(7.0);
    Box1.setHeight(5.0);

    // box 2 
    Box2.setLength(12.0);
    Box2.setBreadth(13.0);
    Box2.setHeight(10.0);

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

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

    return 0;
}

类访问修饰符

         数据封装是OOP的一个重要特点,防止函数直接访问类的内部成员。

         类成员可以被定义为 public、private 或 protected。成员和类的默认访问修饰符是 private。类成员的访问限制,通过在类主体内部对各个区域标记 public、private、protected 来指定。关键字 public、private、protected 称为访问修饰符。且一个类可以有多个 public、protected 或 private 标记区域。每个标记区域在下一个标记区域开始之前,或在遇到类主体结束右括号之前都是有效的。

class Base 
{
public:       // 公有成员

protected:    // 受保护成员

private:      // 私有成员

};

公有成员

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

#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 lines;

    lines.setLength(6.0); 
    cout << "Length of line : " << lines.getLength() << endl;

    /*不使用成员函数设置长度*/ 
    lines.length = 10.0;   
    cout << "Length of line : " << lines.length << endl;

    return 0;
}

         编译和执行上面的代码:

Length of line : 6
Length of line : 10

私有成员

         私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有友元函数可以访问私有成员。默认情况下,类的所有成员都是private。例如,在下面的类中,width 是一个私有成员。

class Box
{
    double width;   //width 是一个私有成员
public:
    double length;
    double getWidth(void);
    void setWidth(double wid);
};

         通常情况下,在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数,如下所示:

#include <iostream>

using namespace std;

class Box
{
public:
    double length;
    double getWidth(void);
    void setWidth(double wid);
private:
    double width;
};

double Box::getWidth(void)  // 成员函数定义
{
    return width;
}

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

int main()
{
    Box box;

    /* 因为 length 是公有的,可以不使用成员函数设置长度 */ 
    box.length = 10.0; 
    cout << "Length of box : " << box.length << endl;

    /* 因为 width 是私有的,所以使用成员函数设置宽度   */ 
    box.setWidth(20.0);  
    cout << "Width of box : " << box.getWidth() << endl;

    return 0;
}

         编译和执行上面的代码:

Length of box : 10
Width of box : 20

受保护成员

         protected成员变量或函数与私有成员相似,不同之处在于protected成员在派生类(即子类)中是可访问的。下面的实例中,从父类 Box 派生一个子类 smallBox,在这里 width 成员可被派生类 smallBox 的任何成员函数访问。

#include <iostream>
using namespace std;

class Box
{
protected:
    double width;
};

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

double SmallBox::getSmallWidth(void)  // 子类的成员函数定义
{
    return width;
}

void SmallBox::setSmallWidth(double wid) // 子类的成员函数定义
{
    width = wid;
}

int main()
{
    SmallBox box;

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

    return 0;
}

         编译和执行上面的代码:

Width of box : 5

在继承中的特点

         OOP中最重要的一个概念是继承,继承允许依据一个类来定义一个新的类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。

         继承允许依据另一个类来定义一个类,为说明继承,首先需要一个基类。当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类去继承一个已有的类的成员即可,这个已有的类称为基类,新建的类称为派生类。继承代表了 is-a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等

         public, protected, private三种继承方式,分别改变了基类成员的访问属性。

  • public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private

  • protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private

  • private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private

         无论哪种继承方式,下面两点都没有改变:

  1.  private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
  2. protected 成员可以被派生类访问。
实例

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;    //正确
        cout << a1 << endl;   //正确
        cout << a2 << endl;   //正确
        cout << a3 << endl;   //正确
    }
public:
    int a1;
protected:
    int a2;
private:
    int a3;
};

class B : public 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;    //正确
        cout << a1 << endl;   //正确
        cout << a2 << endl;   //正确
        cout << a3 << endl;   //正确
    }
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;    //正确
        cout << a1 << endl;   //正确
        cout << a2 << endl;   //正确
        cout << a3 << endl;   //正确
    }
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;
}

类的构造函数

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

#include <iostream>

using namespace std;

class Line
{
public:
    Line();  //构造函数
    void setLength(double len);
    double getLength(void);
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 lines;  //创建类的新对象时执行构造函数

    lines.setLength(6.0);
    cout << "Length of line : " << lines.getLength() << endl;

    return 0;
}

         编译和执行上面的代码:

Object is being created
Length of line : 6

带参数的构造函数

         默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:

#include <iostream>

using namespace std;

class Line
{
public:
    Line(double len);  //构造函数
    double getLength(void);
    void setLength(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 lines(11.0);   // 创建类的新对象时,执行构造函数

    cout << "Length of line : " << lines.getLength() << endl; 

    lines.setLength(6.0);    // 再次设置长度
    cout << "Length of line : " << lines.getLength() << endl;

    return 0;
}

         编译和执行上面的代码:

Object is being created, length = 11
Length of line : 11
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;
}

         假设有一个类 DOG,具有多个字段 X、Y、Z 等需要进行初始化,同理地,可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:

DOG::DOG(double a, double b, double c) : X(a), Y(b), Z(c)
{
	....
}

类的析构函数

         类的析构函数是类的一种特殊的成员函数,在每次删除所创建的对象时执行。

         析构函数的名称与类的名称相同,只在前面加波浪号(~)作为前缀,不返回任何值,也不带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。如下所示:

#include <iostream>

using namespace std;

class Line
{
public:
    Line();   // 构造函数
    ~Line();  // 析构函数
    void setLength(double len);
    double getLength(void);
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

拷贝构造函数

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

  • 使用另一个同类型的对象初始化新创建的对象。

  • 复制对象把它作为参数传递给函数。

  • 复制对象,并从函数返回这个对象。

         注意:如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:

classname(const classname &obj) 
{
   //构造函数的主体
}

         obj 是一个对象引用,该对象是用于初始化另一个对象的。实例:

#include <iostream>

using namespace std;

class Line
{
public:
    Line(int len);           // 构造函数
    Line(const Line& obj);   // 拷贝构造函数
    ~Line();                 // 析构函数
    int getLength(void);
private:
    int* ptr;
};

Line::Line(int len)
{
    cout << "调用构造函数" << endl;

    /* 为指针分配内存 */
    ptr = new int;  //在堆中开辟一块int大小的内存空间,并令指针ptr指向它
    *ptr = len;
}

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
释放内存
释放内存

         对上面的实例稍作修改,通过使用已有的同类型的对象来初始化新创建的对象:

#include <iostream>

using namespace std;

class Line
{
public:
    Line(int len);          // 构造函数
    Line(const Line& obj);  // 拷贝构造函数
    ~Line();                // 析构函数
    int getLength(void);
private:
    int * ptr;
};

Line::Line(int len)
{
    cout << "调用构造函数" << endl;
    
    ptr = new int;   
    *ptr = len;
}

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 line1(10);
    Line line2 = line1;   // 拷贝构造函数调用

    display(line1);
    display(line2);

    return 0;
}

         编译和执行上面的代码:

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

友元函数 

        类的友元函数定义在类外部,但有权访问类的所有私有(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;

        实例

#include <iostream>

using namespace std;

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

void Box::setWidth(double wid)
{
    width = wid;
}

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

int main()
{
    Box box;//创建类的新对象

    box.setWidth(10.0);  // 使用成员函数设置宽度
    printWidth(box);     // 使用友元函数输出宽度

    return 0;
}

         编译和执行上面的代码:

Width of box : 10

内联函数

         内联函数通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方,通过内联函数,编译器试图在调用函数的地方扩展函数体中的代码。

         对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。

         把一个函数定义为内联函数,需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符。

        注意:在类定义中的定义的函数都是内联函数,即使没有使用 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(2, 1) << endl;
	cout << "Max (0,200): " << Max(3, 6) << endl;
	cout << "Max (100,1010): " << Max(88, 999) << endl;

	return 0;
}

         编译和执行上面的代码:

Max (20,10): 2
Max (0,200): 6
Max (100,1010): 999

this 指针

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

         实例:

#include <iostream>
 using namespace std;
 
class Box
{
   public:
      Box(double l=2.0, double b=2.0, double h=2.0) //构造函数定义
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      int compare(Box box)
      {
         return this->Volume() > box.Volume();
      }
   private:
      double length;   
      double breadth;    
      double height;     
};
 
int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    
   Box Box2(8.5, 6.0, 2.0);    
 
   if(Box1.compare(Box2))
      cout << "Box2 is smaller than Box1" <<endl;
   else
      cout << "Box2 is equal to or larger than Box1" <<endl;

   return 0;
}

         编译和执行上面的代码:

Constructor called.
Constructor called.
Box2 is equal to or larger than Box1

指向类的指针

         类可以看成是一个带有函数的结构,指向类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。在使用指针之前,需对指针进行初始化。

         实例:

#include <iostream>
using namespace std;

class Box
{
public:
    Box(double l = 2.0, double b = 2.0, double h = 2.0) // 构造函数定义
    {
        cout << "Constructor called." << endl;
        length = l;
        breadth = b;
        height = h;
    }
    double Volume()
    {
        return length * breadth * height;
    }
private:
    double length;    
    double breadth;   
    double height;     
};

int main(void)
{
    Box Box1(3.3, 1.2, 1.5);    // 声明 box1
    Box Box2(8.5, 6.0, 2.0);    // 声明 box2
    Box * ptrBox;               // 声明指向类的指针.

    ptrBox = &Box1;    // 保存第一个对象的地址
    cout << "Volume of Box1: " << ptrBox->Volume() << endl;    //使用成员访问运算符来访问成员

    ptrBox = &Box2;   // 保存第二个对象的地址
    cout << "Volume of Box2: " << ptrBox->Volume() << endl;    //使用成员访问运算符来访问成员

    return 0;
}

         编译和执行上面的代码:

Constructor called.
Constructor called.
Volume of Box1: 5.94
Volume of Box2: 102

类的静态成员

         类的数据成员和函数成员都可以被声明为静态的。可以使用 static 关键字来把类成员定义为静态的。

         当声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。

class Box
{
public:
    static int objectCount;  //静态变量
    int A_var;               //实例变量
public:
    int funct()
    {
        int A_local;         //局部变量
    }
};

          静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为

          静态成员的初始化不能放置在类的定义中,但可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量,从而对它进行初始化,如下面的实例所示:

 int Box::objectCount = 0;  // 初始化类 Box 的静态成员

         实例: 

#include <iostream>

using namespace std;

class Box
{
public:
    static int objectCount;     //使用 static 关键字来把类成员定义为静态

    Box(double l = 2.0, double b = 2.0, double h = 2.0) 
    {
        cout << "Constructor called." << endl;
        length = l;
        breadth = b;
        height = h;
        objectCount++;          // 每次创建对象时增加 1
    }

    double Volume()
    {
        return length * breadth * height;
    }
private:
    double length;     
    double breadth;   
    double height;     
};

int Box::objectCount = 0;       // 初始化类 Box 的静态成员

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; 
    cout << "Volum of Box1 : " << Box1.Volume() << endl;
    cout << "Volum of Box2 : " << Box2.Volume() << endl;

    return 0;
}

         编译和执行上面的代码:

Constructor called.
Constructor called.
Total objects: 2
Volum of Box1 : 5.94
Volum of Box2 : 102

类的静态成员函数 

         把函数成员声明为静态,就可以把函数与类的任何特定对象独立开来。

        注意静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。

         静态成员函数有一个类范围,他不能访问类的 this 指针。

         可以使用静态成员函数来判断类的某些对象是否已被创建。

         静态成员函数与普通成员函数的区别:

  • 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
  • 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。

         实例:

#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;
        objectCount++;  // 每次创建对象时增加 1
    }

    double Volume()
    {
        return length * breadth * height;
    }

    static int getCount()  //声明静态成员函数
    {
        return objectCount;
    }
private:
    double length;     
    double breadth;    
    double height;     
};

int Box::objectCount = 0;  // 初始化类 Box 的静态成员

int main(void)
{
    //在创建对象之前输出对象的总数
    cout << "Inital Stage Count: " << Box::getCount() << endl;  

    Box Box1(3.3, 1.2, 1.5);    // 声明 box1
    Box Box2(8.5, 6.0, 2.0);    // 声明 box2

     //在创建对象之后输出对象的总数
    cout << "Final Stage Count: " << Box::getCount() << endl;  

    return 0;
}

         编译和执行上面的代码:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 类 & 对象 的相关文章

随机推荐

  • globbing

    1 globbing是什么 globbing表示通配符 BASH支持文件名通配 2 globbing常用列表及使用心得 序号 符号 使用心得 1 表示任意一个字符 注意与常规的正则表达式的区别 正则中 表示可选的 2 表示任意长度任意字符
  • STM32HAL----USB模拟串口(VCP)

    想要实现的功能是 USB模拟串口收发数据 串口助手发送数据至MCU MCU接收后返回给串口助手 当初是想用标准库做这个功能的 但是因为后来了解到STM32CubeMX这个软件 在尝试之后实在是感觉 太方便了 所以 并没有使用标准库 而是直接
  • Python---函数

    PP2study5 一 函数定义 二 函数调用 三 可变对象和不可变对象的使用 四 参数传递 4 1 以下是调用函数时可使用的正式参数类型 1 必需参数 2 关键字参数 3 默认参数 4 不定长参数 4 2 return 一 函数定义 Py
  • autoReconnect及查看和连接时间有关的系统变量与通常产生的异常

    MySQL官方不推荐使用autoReconnect true 参见http bugs mysql com bug php id 5020 注意这里说的版本是3 0 14 production 需要另外找别的办法来解决超过8小时 链接断开的问
  • 【数据结构和算法】字符串操作

    作者 Linux猿 简介 CSDN博客专家 华为云享专家 Linux C C 云计算 物联网 面试 刷题 算法尽管咨询我 关注我 有问题私聊 关注专栏 数据结构和算法成神路 精讲 优质好文持续更新中 欢迎小伙伴们点赞 收藏 留言 目录 一
  • CAsyncSocket进行UDP通信

    CAsyncSocket进行UDP通信 客户端代码 CString m ServerIP CString m ClientIP int m ClientPort CString m ReceiveData UINT m ServerPort
  • 基于用户的协同过滤算法(及3种计算用户相似度的方法)

    本文参考 推荐系统实践 中基于用户的协同过滤算法内容 基于老师上课讲解 自己实现了其中的代码 了解了整个过程 UserCF算法实现 实现原理 模拟数据 两两用户之间计算 优化后的倒查表方式计算用户相似度 采用惩罚热门物品和倒查表方式计算用户
  • vue+websocket+express+mongodb实战项目(实时聊天)(一)

    vue websocket express mongodb实战项目 实时聊天 一 在原来基础上增加了多个聊天室以及发送图片 vue websocket express mongodb实战项目 实时聊天 二 http blog csdn ne
  • ARM中的程序状态寄存器(CPSR)

    31 30 29 28 27 8 7 6 5 4 3 2 1 0 N Z C V 保留 I F T M4 M3 M2 M1 M0 N Negative Less Than I IRQ disable Z Zero F FIQ disable
  • Caffe在Linux下的安装,编译,实验

    第一部分 Caffe 简介 caffe是有伯克利视觉和学习中心 BVLC 开发 作者是伯克利博士贾杨清 caffe是一个深度学习 deep learning 框架 其具有易读 快速和模块化思想 第二部分 Caffe安装与配置 2 1 配置环
  • 彻底卸载、devtools安装问题、扩展程序的使用

    目录 一 彻底卸载 MySQL Mongo数据库 二 vue devtools 三 扩展程序的使用 一 彻底卸载 MySQL Mongo数据库 停止服务1 cmd命令 net stop mysql mongo 停止服务2 如下图 这个好 控
  • ElasticSearch笔记整理(三):Java API使用与ES中文分词

    TOC pom xml 使用maven工程构建ES Java API的测试项目 其用到的依赖如下
  • Packing data with Python

    Packing data with Python 06 Apr 2016 Defining how a sequence of bytes sits in a memory buffer or on disk can be challeng
  • EasyAR 开发实例---AR礼物(简单demo)

    一个节日礼物效果 显示模型 在本次的案例中 我使用的是unity5 6 3版本 EasyAR 为2 0 用1 0的版本 在渲染那块有问题 导入SDK 到EasyAR官网 http www easyar cn view download ht
  • minitab数据处理软件

    下载地址 http www xue51 com soft 3430 html 1 介绍 Minitab软件是现代质量管理统计的领先者 全球六西格玛实施的共同语言 以无可比拟的强大功能和简易的可视化操作深受广大质量学者和统计专家的青睐 Min
  • Springboot 指定日志打印文件夹

    日志打印作为日常开发是必不可少了 SpringBoot项目中引入spring boot starter 这里面就包含了日志所需要的依赖 下面是两种方法打印日志 都很简单 亲测有效 方法一 直接在application yml中添加配置 指定
  • java 获取两个List集合的交集

    获取两个List集合的交集 可以使用Java中的retainAll方法来获取两个List的交集 假设有两个List类型的集合list1和list2 代码如下 List
  • MySQL主主复制+Keepalived 打造高可用MySQL集群

    转载地址 http www linuxidc com Linux 2014 09 106570 htm 为了响应公司需求 打造出更安全的mysql集群 能够实现mysql故障后切换 研究了几天终于有了成果 一起分享一下 首先介绍一下这套集群
  • 代码和数据结构

    代码 58同城 给出任意一个正整数 怎么用递归把他反过来打印 include
  • C++ 类 & 对象

    C 在 C 的基础上增加了面向对象编程 OOP 支持面向对象程序设计 类是 C 的核心特性 一种用户自定义的类型 用于指定对象的形式 类包含数据和用于处理数据的方法 函数 数据称为成员变量 函数称为成员函数 类可以看作是一种模板 用来创建具