从小白开始学C++ 类与对象一(创建、定义类、构造函数、析构函数)


今天就是C++与C最大的不同之处了
内容大致包括

  1. 过程性编程和面对对象编程的区别
  2. 类的概念
  3. 如何定义和实现类
  4. 类的成员函数的定义
  5. 创建和使用类对象
  6. 类的构造函数和析构函数

1. 过程性编程和面对对象编程的区别

学过C的同学们都知道,C语言是过程性编程语言,我们设计一个程序的时候首先考虑的是要遵循的步骤。
比如我们要设计统计一个球队的数据,我们首先想到的是我们需要统计的数据有哪些:球员的姓名,号码,命中率等,然后对于这些数据我们给出一些处理方法——函数
那么自然我们会想到用结构体来封装球员的信息,然后定义函数来求平均值或者最大值,再在main函数中给出菜单来实现相应的功能

但是我们从OOP的角度来考虑呢?我们的基本数据元素单元应该是——每一个球员,这个对象中我们不仅要包含基本的数据比如姓名和号码,我们同样可以将一些执行的功能添加,方便调用。

采用OOP方法时,首先从用户的角度考虑对象——描述对象所需的数据以及描述用户与数据交互所需的操作

2. 类的概念

我们需要从复杂的概念中抽象出类,它将数据表示和操纵数据的方法组合成一个整洁的包。
一般来说,类规范由两个部分组成:
类声明:以数据成员的方式描述数据部分,以成员函数(被成为方法)的方式描述公有接口
类方法定义:描述如何实现类成员函数

3. 如何实现和定义类

首先是类的声明,我们用class来定义一个类设计

#include<iostream>
class Dog //这是一个类的声明
{
private://私有
	char name[30];
	int age;
	char color[20];
public://公有
	void howl(int time);
	void running(int time,char location[]);
	void show();
};

以上就是一个简单的类的声明
我们已经介绍了class,这里还有两个东西是新的:public,private
他们描述了对类成员的访问控制。
使用类对象的程序都可以直接访问公有部分,也就是public定义的部分
在上面就是两个函数howl、running、show;
但只能通过公有成员函数(例子中也就是howl、running和show)
或者友元函数(下次博客会提到) 来访问对象的私有成员(private)
也就是说我们如果想要修改private里面各个变量的值,我们必须通过public中的函数来修改
也就是说公有成员函数是程序和对象的私有成员的桥梁,提供了对象和程序之间的接口。
防止程序直接访问数据被称为数据隐藏

ps:private是可以省略的,因为这是类对象的默认访问控制

所以你也可以这样写:

class Dog //这是一个类的声明
{
	char name[30];
	int age;
	char color[20];
public:
	void howl(int time);
	void running(int time,char location[]);
	void show();
};

4. 实现类成员函数

我们需要为那些由类声明中的原型表示的成员函数提供具体代码

这里的函数定义与普通的函数定义类似,有函数体和函数头,也可以有返回类型和参数,但是由于他们是类中的方法所以它们还有以下两个特征:

  1. 定义成员函数时,必须使用作用域操作符::来标识函数所属的类
  2. 类方法可以访问类的private内的成员

所以类的第二部分我们可以这样来编写:

void Dog::howl(int time)
{
    cout << name <<" has howled for " << time <<" seconds\n";
}
void Dog::running(int time,char location[])
{
    cout << name <<" has ran for "<<time<<" seconds in the "<<location;
}
void Dog::show()
{
    cout << "please input the dog's name,age and color:\n";
    cin >> name;
    cin >> age;
    cin >> color;
    cout << "name : " <<name<<"\n";
    cout << "age : "<<age<<"\n";
    cout << "color : "<<color << "\n";
}

5. 创建和使用类对象

最后我们加上main函数来实例化这个类

完整代码:

#include <iostream>
using namespace std;

class Dog //这是一个类的声明
{
private://私有
	char name[30];
	int age;
	char color[20];
public://公有
	void howl(int time);
	void running(int time,char location[]);
	void show();
};
//成员函数是可以调用私有成员的
void Dog::howl(int time)
{
    cout << name <<" has howled for " << time <<" seconds\n";
}
void Dog::running(int time,char location[])
{
    cout << name <<" has ran for "<<time<<" seconds in the "<<location;
}
void Dog::show()
{
    cout << "please input the dog's name,age and color:\n";
    cin >> name;
    cin >> age;
    cin >> color;
    cout << "name : " <<name<<"\n";
    cout << "age : "<<age<<"\n";
    cout << "color : "<<color << "\n";
}

int main()
{
    Dog dog;//创建一个实例化的类对象
    dog.show();//通过.来调用方法
    dog.howl(30);
    char location[30]="playground";
    dog.running(60,location);
    return 0;
}

输出结果:

please input the dog's name,age and color:
Peter
2
white
name : Peter
age : 2
color : white
Peter has howled for 30 seconds
Peter has ran for 60 seconds in the playground

这样我们就完成了整个类的构建

6. 类的构建函数和析构函数

我们会发现一个问题——上面的代码中我们初始化私有成员的值时是通过调用函数来实现的,那么我们是否可以像初始化int 型变量一样来初始化类的实例呢?

答案是否定的——聪明的小伙伴们肯定已经知道了原因

上述例子中我们的数据是定义再private中的,数据部分的访问状态是私有的——也就意味着程序是无法访问数据成员的——当然也就无法通过程序进行初始化了

而且上述代码有一个致命的缺陷

如果我们在show()函数调用之前就调用了另外两个方法howl或者running,这时我们的私有成员name、age、color是没有值的

所以我们有没有什么方法能够在创建对象时,自动就对它初始化呢?

没错那就是下面要介绍的构造函数
(在python中我们有方法__init__来初始化)
(C++中我们使用构造函数)

构造函数
专门用来构造新对象,将值赋给他们的数据成员的一类函数

最重要的特征:名称与类名相同!名称与类名相同!名称与类名相同!

所以针对上面给出的代码,我们给出它的一个构造函数

Dog :: Dog(char name[],int age, char color[])//给出构造函数
{
    strncpy(m_name, name,30);//这里使用了C++的一个库cstring来复制字符串
    m_age = age;
    strncpy(m_color,color,20);
}

注意这样一个问题,我们或许习惯用类成员名称作为构造函数的参数,但是如果我们这样做,在复制时就会出现age=age这样的混乱,所以为了避免这样的赋值,我们一般在成员的前面加一个前缀
——这里参考了C++Primer Plus的做法

有了构造函数,我们在声明实例的时候就可以将初始值赋给新对象了

我们一般有两种赋值方法:显式和隐式,在下面的代码中我会给出两种赋值的方法供参考

注意一般我们会给所有类成员做隐式初始化的默认构造函数
也就是说当我们不提供参数的时候,我们一样可以做隐式初始化——这里用到了函数重构的方法

析构函数

用构造函数创建对象后,当对象过期时,程序将自动调用一个特殊的成员函数——析构函数来完成清理工作

既然系统会自动调用,为什么还需要我们来编写析构函数呢?
——如果构造函数使用了new来分配内存,则析构函数应该使用delete来释放这些内存

析构函数的名称也很有趣:在类名前加~
特征:析构函数没有参数!析构函数没有参数!析构函数没有参数!
由于上面的代码中我们没有动态分配空间,所以析构函数不承担任何重要的工作,但是为了显示我们调用了它,我们这样编写析构函数:

Dog :: ~Dog()
{
    //这是一个析构函数
    cout << "bye! My dear dog, "<<m_name<<"\n";
}

什么时候调用析构函数呢?——这由编译器来决定,通常我们不会显示地调用析构函数,一般在程序结束的时候,系统会自动调用析构函数

7. 完整代码展示

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

class Dog //这是一个类的声明
{
private://私有
	char m_name[30];
	int m_age;
	char m_color[20];
public://公有
    Dog();
    Dog(char name[],int age,char color[]);
    ~Dog();
	void howl(int time);
	void running(int time,char location[]);
	void show();
};

Dog :: Dog()//不提供参数时的默认构造
{
    strncpy(m_name, "no_name",30);
    m_age=0;
    strncpy(m_color,"unknown",20);
}

Dog :: ~Dog()
{
    //这是一个析构函数
    cout << "bye! My dear dog, "<<m_name<<"\n";
}

Dog :: Dog(char name[],int age, char color[])//提供参数时的构造函数
{
    strncpy(m_name, name,30);
    m_age = age;
    strncpy(m_color,color,20);
}

void Dog::howl(int time)
{
    cout << m_name <<" has howled for " << time <<" seconds\n";
}

void Dog::running(int time,char location[])
{
    cout << m_name <<" has ran for "<<time<<" seconds in the "<<location<<"\n";
}

void Dog::show()
{
    cout << "name : " <<m_name<<"\n";
    cout << "age : "<<m_age<<"\n";
    cout << "color : "<<m_color << "\n";
}

int main()
{
    Dog dog1("Peter",2,"white");//隐式调用构造函数
    dog1.show();
    dog1.howl(30);
    char location[30]="playground";
    dog1.running(60,location);
    Dog dog2 = Dog();//这里时显式调用 注意默认参数无法进行隐式调用
    dog2.show();
    return 0;
}

输出结果

name : Peter
age : 2
color : white
Peter has howled for 30 seconds
Peter has ran for 60 seconds in the playground
name : no_name
age : 0
color : unknown
bye! My dear dog, no_name//程序结束是自动调用析构函数
bye! My dear dog, Peter

关于后续的this指针、对象数组、友元函数等内容后续会有更新

点个赞再走呀

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国家一级假勤奋研究牲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值