C++类(未完结)

本文介绍了C++中的类和面向对象编程的基本概念,包括类的定义、属性和方法,以及面向对象的三大特性——封装、继承和多态。详细讲解了构造函数、拷贝构造函数、移动构造函数的作用,以及this指针的使用。还讨论了const对象和const成员函数在保证数据安全方面的重要性。
摘要由CSDN通过智能技术生成

类的特点

类,c++程序员的工具箱中最基础且最重要的工具之一。是面向对象编程的基础。

  • 什么是类?

    类,是用户自定义的数据类型,类似结构体(struct)。在c语言中我们常使用struct,而在c++中我们用类比较多。

面向对象

面向对象编程(通常缩写为OOP)的本质是,根据需要解决的问题涉及的对象来编写程序。因此程序开发过程的一部分就是涉及一组类型来满足这个要求。

两大成员

属性

对象的某些特征数据,比如一个学生对象,我们可能需要定义他的姓名,学号、各科成绩等。同理,对于一个立方体,我们可能需要定义它的长、宽、高。

方法

类中提供的一些函数,比如学生对象,可以提供一个info函数,输出学生信息。sumGrade方法,返回学生总成绩的。立方体对象,我们就可以设计两个函数(方法),分别计算表面积和体积。

三大特性

封装

类的一些函数和方法,我们不想让它们在类外直接访问(通过对象直接),就可以把它们封装起来。比如学生的成绩,只允许查询而不允许修改,我们就可以把成绩封装起来,不能直接拿到学生成绩,而是通过我们定义的没被封装的函数(方法)来访问。而我们不提供修改的方法,这样就实现了只允访问,而不能修改。总结一句话,对于封装的数据,禁止直接访问该数据,但可以通过对象的成员函数来访问。

类的所有成员默认为私有,表示不能在类的外部直接访问他们。

实现:

通过c++为我们提供的public、protected、private关键字可以很轻松的实现。

继承

根据一个类定义另一个类。比如,我们先定义一个human类,属性有姓名、性别、年龄、家庭住址等。我们此时又想定义一个学生类,此时我们就可以通过继承human来继承human类的姓名、性别、年龄、家庭住址等属性,然后在另外定义成绩、学号属性就行。这样就不需要在再重新定义属性。

多态

多态表示在不同时刻有不同的形态。c++中的多态性总是设计使用指针或引用来调用对象对象的成员函数。这个说起来有些复杂,之后给代码演示。

类的定义

类是用户自定义数据类型。使用class关键字定义类。

class ClassNamed
{
	// 属性
    // 方法
};  // ; 不可省

定义了一个类,类的名称为ClassName。类名首字母一般为大写字母。

类的成员(方法、属性)都在类中定义(方法的实现可放在类外)。

类的成员函数默认为私有,在类外不能直接访问。可以使用public关键字后跟一个冒号,表示后面定义的成员都能在类外直接访问(直至遇到下一个private:或protected:)。

class ClassName
{
private:
    // 私有成员。定义在外部不能直接访问的成员。
    // something...
    
public:
    // 公开成员。定义在外部能直接访问的成员。
    // something...
};

下面我们定义一个立方体盒子

class Box
{
 private:
    double length;	// 盒子的长
    double width;   // 盒子的宽
    double height; 	// 盒子的高
    
public:
    double get_volume() // 成员函数(方法),计算Box体积
    {
        double volume = length * width * height;	// 计算体积
        return volume;							 // 返回体积
    }
};

类和结构体的区别

区别:C++类的成员默认是私有的,C++结构的成员默认是公开的。其他各方面,几乎一样。

在C++我们常使用类,而结构体一般是定义一些小型对象。结构体可以直接放在类中,作为类的成员属性。

类的构造函数

  1. 构造函数,作用是用来构造类的对象(类似用int定义变量。int a=2; a就是一个int型的对象。)。

  2. 本质都是一种函数,可以有参数但没有返回值。

  3. 没有返回值是指,我们不给出返回值,不是指返回值为空,这是一种规定。

默认调用函数和无参构造函数

class Box
{
 private:
    double length;	// 盒子的长
    double width;   // 盒子的宽
    double height; 	// 盒子的高
    
public:
   	Box()	// 无参构造函数
    {
        length = 1.0;
        width = 1.0;
        height = 1.0;
        cout << "调用无参构造函数" << endl;
    }
};
  1. 默认构造函数没有参数,没有参数,没有返回值。一定是没有,而不是返回值为空。
  2. 如果类没有任何构造函数,编译器会自动生成一个默认的默认构造函数。
  3. 如果类已经定义了构造函数,此时编译器不会给出默认构造函数,此时我们需要定义无参构造函数(或者显式定义无参构造函数)。否则Box box1;错误。
// 测试构造函数
#include <iostream>
using namespace std;
class Box
{
 private:
    double length;	// 盒子的长
    double width;   // 盒子的宽
    double height; 	// 盒子的高
    
public:
    // Box () = default; 
   	Box()	// 默认构造函数
    {
        length = 1.0;
        width = 1.0;
        height = 1.0;
        cout << "调用无参构造函数" << endl;
    }
public:
    double get_volume() // 成员函数(方法),计算Box体积
    {
        double volume = length * width * height;	// 计算体积
        return volume;							 // 返回体积
    }
};

int main()
{
    Box box1;	// 调用无参构造函数,构造对象box1
    cout << box1.get_volume() << endl;	
    return 0;
}

/*output
	调用无参构造函数
	1
*/

一般构造函数

class Box
{
 private:
    double length;	// 盒子的长
    double width;   // 盒子的宽
    double height; 	// 盒子的高
    
public:
   	Box()	// 无参构造函数
    {
        length = 1.0;
        width = 1.0;
        height = 1.0;
    }
    
    Box(double _len, double _wid, double _hei)	// 一般构造函数1
    {
        length = _len;
        width = _wid;
        height = _hei;
    }
    
    Box(double _len, double _wid)	// 一般构造函数2
    {
        length = _len;
        width = _wid;
        height = 1.0;
    }
};
  1. 一般构造函数可以不止一个。

  2. 一般构造函数通过函数参数来区分。

  3. 一般构造函数定义对象。Box box2(参数1,参数二, 。。)

// 测试一般构造函数
#include <iostream>
using namespace std;
class Box
{
 private:
    double length;	// 盒子的长
    double width;   // 盒子的宽
    double height; 	// 盒子的高
    
public:
   	Box()	// 无参构造函数
    {
        length = 1.0;
        width = 1.0;
        height = 1.0;
        printf("无参构造函数被调用\n");
    }
    
    Box(double _len, double _wid, double _hei)	// 一般构造函数1
    {
        length = _len;
        width = _wid;
        height = _hei;
        printf("构造函数 Box(double _len, double _wid, double _hei) 被调用\n");
    }
    
    Box(double _len, double _wid)	// 一般构造函数2
    {
        length = _len;
        width = _wid;
        height = 1.0;
        printf("构造函数 Box(double _len, double _wid) 被调用\n");
    }
    
public:
    double get_volume() // 成员函数(方法),计算Box体积
    {
        double volume = length * width * height;	// 计算体积
        return volume;							 // 返回体积
    }

};

int main()
{
    Box box1;	                // 调用默认构造函数
    Box box2(2.0, 3.0);         // 调用一般构造函数2
    Box box3(2.0, 3.0, 4.0);    // 调用一般构造函数1
    
    cout << "box1: " << box1.get_volume() << endl;	
    cout << "box2: " << box2.get_volume() << endl;	
    cout << "box3: " << box3.get_volume() << endl;	
    
    return 0;
}

/*output
	无参构造函数被调用
    构造函数 Box(double _len, double _wid) 被调用
    构造函数 Box(double _len, double _wid, double _hei) 被调用
    box1: 1
    box2: 6
    box3: 24
*/

拷贝构造函数

如果我们需要通过一个现有对象,去构造一个新的相同的对象,此时我们就需要使用到拷贝构造函数。

class Box
{
 private:
    double length;	// 盒子的长
    double width;   // 盒子的宽
    double height; 	// 盒子的高
    
public:
   	Box()	// 无参构造函数
    {
        length = 1.0;
        width = 1.0;
        width = 1.0;
    }
    
    Box(double _len, double _wid, double _hei)	// 一般构造函数
    {
        length = _len;
        width = _wid;
        height = _hei;
    }

    Box(const Box& box)	// 拷贝构造函数
    {
        length = box.length;
        width = box.width;
        height = box.height;
    }
};
  1. 拷贝构造函数的参数是一个常量引用。
// 测试拷贝构造函数
#include <iostream>
using namespace std;
class Box
{
 private:
    double length;	// 盒子的长
    double width;   // 盒子的宽
    double height; 	// 盒子的高
    
public:
   	Box()	// 无参构造函数
    {
        length = 1.0;
        width = 1.0;
        height = 1.0;
        printf("无参构造函数被调用\n");
    }
    
    Box(double _len, double _wid, double _hei)	// 一般构造函数1
    {
        length = _len;
        width = _wid;
        height = _hei;
        printf("构造函数 Box(double _len, double _wid, double _hei) 被调用\n");
    }
    
    Box(const Box& box)	// 拷贝构造函数
    {
        length = box.length;
        width = box.width;
        height = box.height;
        printf("拷贝构造函数被调用\n");
    }
    
public:
    double get_volume() // 成员函数(方法),计算Box体积
    {
        double volume = length * width * height;	// 计算体积
        return volume;							 // 返回体积
    }

};

int main()
{
    Box box1;	                // 调用默认构造函数
    Box box2(2.0, 3.0, 4.0);    // 调用一般构造函数
    Box box3(box2);            // 调用拷贝构造函数
    
    cout << "box1: " << box1.get_volume() << endl;	
    cout << "box2: " << box2.get_volume() << endl;	
    cout << "box3: " << box3.get_volume() << endl;	
    
    return 0;
}

/*output
    无参构造函数被调用
    构造函数 Box(double _len, double _wid, double _hei) 被调用
    拷贝构造函数被调用
    box1: 1
    box2: 24
    box3: 24
*/

移动构造函数

  1. 用一个现有对象去创建一个新对象。
  2. 一般当对象管理者堆上的属性时,才会用到。
class A
{
 private:
    int *ptr;
    
public:
   	A() {ptr = new int();}	// 无参构造函数
    
    A(int *_p)	{ptr = _p;}// 一般构造函数
    
    A(const A& _a)	// 拷贝构造函数
    {
		ptr = new int( *(_a.ptr) );
    }
    
    A(A&& _a)  	// 移动构造函数
    {
		ptr = _a.ptr;
        _a.ptr = nullptr;
    }
    
    ~A()	// 析构函数 后面说
    {
        if (ptr != nullptr)
        	delete ptr;
    }
};
  1. 看不明白也没关系,这里没有细讲。
  2. 移动构造函数可以不着急掌握。等后面对类很熟悉后,在了解也不晚,不影响类的使用。

类的析构函数

当我们调用构造函数创建对象时,当超出这个对象作用域时,会调用构造函数将对象析构。

class A
{
 private:
    int *ptr;
    
public:
   	A() {ptr = new int();}	// 无参构造函数
    
    A(int *_p)	{ptr = _p;}// 一般构造函数
    
    Box(const A& _a)	// 拷贝构造函数
    {
		ptr = new int( *(_a->ptr) );
    }
    
    ~Box()	// 析构函数
    {
        if (ptr != nullptr)
        	delete ptr;
    }
};
  1. A的对象在构造之处,使用new在堆上创建了int数据。我们知道,new出的数据,一定要调用delete删除。

  2. c++编译器不会自动调用delete,需要手动调用。所以我们在析构函数进行此操作。

2023/6/17更新

this指针

简单使用

在执行任何成员函数时,该成员函数都自动包含一个隐藏的指针,称为this指针。

this指针指向此成员函数的对象本身,存放的是该成员函数的地址。

比如,我们有时候会写出这样的成员函数。

class A
{
private:
   int age; 
public:
    void setAge(int age)  // 设置 age, 函数参数为了好理解, 也同属性名一样的 age
    {
        // age = age;  // 失败
        this->age = age;
    }
    
    /*
    void setAge (int newAge) 
    {
    	// do something
    	int age = 456;
    	//do somethings
    	// age = newAge; // 还是不行,编译器分辨不清这是函数中定义的临时变量还是私有属性age。
    	this->age = newAge; // 正确。 this指向的变量专指属性,不存在任何歧义。
    }
    */
    
    void printAge() { cout << age << endl; }
};

为设置私有属性 age 的值,我们需传入新的age值。如果我们把setAge参数名也设置为age。如果不借助this指针,我们只能这样写,age = age, 但测试后发现这样是错误的,无法成功更改age值,因为编译器不知道这两个age分别代表什么。

此时,this也就排上用场了, this->age = age; 用this->age特指对象的age属性,与传入的age参数区分开来,设置成功。

this指针确实在成员函数中存在,且给我们提供一种没有歧义的设置属性的手段。

函数返回对象指针和引用

this 是指向此对象的指针。 *this是对象本身。

借助 this指针实现 "方法链"。类似这种形式 ObjectName.func1().func2().func3()

#include <iostream>
using namespace std;

class Student
{
private:
    int age;
    char sex;
    string name;
    
public:
    Student& setAge (int _age) // 返回对象引用
    {
        this->age = _age;
        return *this;
    }
    
    Student & setSex (char _sex)
    {
        this->sex = _sex;
        return *this;
    }
    
    Student & setName (const string& _name)
    {
        this->name = _name;
        return *this;
    }

    void printInfo () 
    {
        cout << "姓名: " << name << endl;
        cout << "性别: " << sex << endl;
        cout << "年龄: " << age << endl;
    }
};

int main()
{
    Student stu;

    stu.setAge(19).setName("张三").setSex('M');
    stu.printInfo();

    return 0;
}

/*output
	姓名: 张三
    性别: M
    年龄: 19
*/

const

const对象和const成员函数

const 变量是不能修改其值的变量。自然,也可以定义类类型的const变量。这种变量称为const对象。构成const对象状态的任何成员变量不能被修改。

也就是说,const 对象的任何成员变量本身是一个 const 变量,因而不能修改。

class Box
{
public:
    double length;
    double width;
    double height;
    
    // 使用默认生成的默认无参构造函数
    Box() = default;
    
    // 构造函数
    Box (double _len, double _wid, double _hei) : length(_len), width(_wid), height(_hei) {} 
    
    // 求体积
    double getVolume() { return length * width * height ;}
};

Box类的长宽高是公有的,是可以直接通过对象名修改的。例如

Box b1(1, 2, 3);
b1.length = 1.6;
b1.width = 1.5;
b1.height = 1.8;

如果想创建一个不可被修改的Box对象,可借助const。

const Box b2(1, 2, 3);
cout << b2.length << endl; // ok
// b2.length = 1.6;   // error
// b2.width = 1.5;    // error
// b2.height = 1.8;   // error

同样const也可作用域指针和引用。

Box b1(1, 2, 3);
const Box* ptr = &b1;
// ptr->length = 2.3;  // error 

void printBox(const Box& box);	// 在printBox函数中也不可修改box的值

const 成员函数

上面我们通过 const Box b2(1, 2, 3); 定义了一个Box的const对象,此时我们我们不在能修改成员变量,一定程度上保证了数据的安全性。但也带来了一个问题:getVolume()函数也将不可调用。

为什么?因为编译器并不知道 getVolume() 是否修改了成员变量,此时b2.getVolume();编译通不过。

解决方案:将getVolume函数设置成const 成员函数。

// 修改后的 getVolume
double getVolume() const  // const 关键字标识此函数不修改任何成员变量
{ return length * width * height ;}

// b2.getVolume(); 将能通过编译。

对于const对象,只能调用 const 成员函数。因此,应该将不修改对象的所有成员函数指定为const。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值