C++ ---- 类初涉

什么是类?

类的基本思想是数据抽象封装

数据抽象是一种依赖于接口和实现分离的编程(以及设计)技术。类的接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。

封装实现了类的接口和实现的分离。封装后的类隐藏了它的实现细节,也就是说,类的用户只能使用接口而无法访问实现部分。

类要想实现数据抽象和封装,需要首先定义一个抽象数据类型。在抽象数据类型中,由类的设计者负责考虑类的实现过程;使用该类的程序员则只需要抽象地思考类型做了什么,而无须了解类型的工作细节。


C++ 中使用关键字 class 来定义类, 其基本形式如下:

class 类名
{
    // 行为或属性 (默认)      
    public:

        //行为或属性 

    protected:

        //行为或属性

    private:

        //行为或属性

};

以上对比与 c 中个结构体 struct 十分相似。然而,C++对结构进行了扩展,使之具有相同的特性。它们之间唯一的区别就是,结构体的默认访问类型(即不包含在以上三种类型中)是 public,而类为 private。C++程序猿通常使用类来实现类的描述,而把结构体作为纯粹的数据对象表示。

在C++程序中通常使用访问控制关键字来保证自身属性的可见性。常见的三个关键字为 private,public,protected

关键字含义

private:C++中的默认访问权限,也就是说上述student.h文件中的private关键字可以省略。其含义表示为只有该对象自身可以访问这些属性或者方法,被private修饰的属性和方法对于外部来说不可见
public:公有权限,表示被public修饰的属性和方法可以被外部程序直接调用
protected :受保护的权限,表示这些属性和方法仅仅能被该类和派生类的内部访问,也就是说,对于外部世界来说,保护成员的行为与private相同,对于派生类来说,保护成员的行为和public相同

 

作用域解析运算符

在具体实现一个类之前,我们需要先了解一个运算符:作用域解析运算符(::)。该符号的作用是用来标识一个函数所属的类,例如当我们输入Student :: average()时。表示我们访问的average()方法属于Student类而不是其他的类。 
但是在一个Student类的内部我们并不需要使用使用作用域解析运算符就能访问average()方法,这是因为在类的内部自身所定义的所有方法对自身来说都是可见的。 
对于一个方法名称来说,Student :: average()是方法的函数限定名(全称),而average()方法属于非限定名(简称),当程序对于一个函数来源无异议时(没有重名方法),可以使用简称。

 

类声明与类定义

类声明(declare)

class Screen;

      在声明之后,定义之前,只知道 Screen 是一个类名,但不知道包含哪些成员。只能以有限方式使用它,不能定义该类型的对象,只能用于定义指向该类型的指针或引用,声明(不是定义)使用该类型作为形参类型或返回类型的函数。

void Test1(Screen& a){};
void Test1(Screen* a){};

类定义(define)
     在创建类的对象之前,必须完整的定义该类,而不只是声明类。所以,类不能具有自身类型的数据成员,但可以包含指向本类的指针或引用。

class LinkScreen
{
public:
          Screen window;
          LinkScreen* next;
          LinkScreen* prev;
}; //注意,分号不能丢

因为在类定义之后可以接一个对象定义列表,可类比内置类型,定义必须以分号结束:

class LinkScreen{ /* ... */ };
class LinkScreen{ /* ... */ } scr1,scr2; 

类对象

定义类对象时,将为其分配存储空间。

Sales_item item; //编译器分配了足以容纳一个 Sales_item 对象的存储空间。item 指的就是那个存储空间。

类的访问限制

public,private,protected 为属性/方法限制的关键字。
类的数据成员中不能使用 autoexternregister等进行修饰, 也不能在定义时进行初始化
如:

int xPos = 0; //错;

例外:
静态常量整型(包括char,bool)数据成员可以直接在类的定义体中进行初始化,例如:

static const int ia= 30; 

 

通常,C++程序将接口(类定义)放在头文件中,并将实现(类方法的代码放在源代码文件中),本文以一个学生的例子来完成代码。

学生定义:记录三门课程(语文,英语,数学)的成绩,能够查看学生的平均成绩


创建头文件
现在创建名为 student.h 的头文件

//Student.h -- Student class interface
//version 00
#ifndef Student_H_
#define Student_H_


#include <string>

class Student{ //class declaration
private:
	std::string name;
	int ch;
	int en;
	int math;
	float average;
	void count(){
		average = (ch + en + math + 0.0F) / 3;
	}

public:
	Student();//构造函数
	~Student();//析构函数
	void setName(std::string name);
	void setChScore(int score);
	void setEnScore(int score);
	void setMathScore(int score);
	void show();
};

#endif

默认的内联方法
 

内联函数有三种:

(1)直接在类内部定义。
(2)在类内部声明,加上inline关键字,在类外部定义。
(3)在类内部声明,在类外部定义,同时加上inline关键字。注意:此种情况下,内联函数的定义通常应该放在类定义的同一头文件中,而不是在源文件中。这是为了保证内联函数的定义在调用该函数的每个源文件中是可见的。

我们在类的定义中声明并定义了一个count()方法,由于其位于声明中,编译器将其编译为内联方法。一般内容简单的方法都可以作为内联方法。如果我们并不想直接在定义中声明并定义一个内联方法,我们也可以在实现时给方法添加 inline 关键字。比如

    inline void count(){
        average = (ch + en + math+0.0F)/3;
    }


但是我们在声明外定义内联函数时必须注意C++中的一个规则:要求内联函数在每一个使用它的源文件中均要定义。也就是说我们需要在每一个使用该方法的文件中定义该方法,很明显我们并不想这样做,所以最好的实现就是在声明中实现定义方法(参考函数 count,在class 中声明和定义)。

 

现在创建名为student.cpp的 c 文件:

//Student.cpp -- implementing the Student class
//version 00

#include "stdafx.h"
#include "Student.h"
#include <iostream>
using namespace std;

Student::Student(){
}

Student::~Student(){
}

void Student::setChScore(int score){
	Student::ch = score;	// 在类内部使用 可省略 Student,无需显示作用域 
}

void Student::setName(std::string n){
	Student::name = n;
}

void Student::setEnScore(int score){
	en = score;
}

void Student::setMathScore(int score){
	math = score;
}

void Student::show(){
	Student::count();

	cout << name << " 同学的语文成绩为" << ch << "分,数学成绩为" << math << "分,英语成绩为" << en << "分,平均成绩" << average << "分" << endl;

}


主程序如下:

// class.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "Student.h"
#include <iostream>
#include "stdlib.h"
using namespace std;


int _tmain(int argc, _TCHAR* argv[]){

	Student *jack = new Student();
	jack->setName("jack");
	jack->setChScore(98);
	jack->setMathScore(100);
	jack->setEnScore(92);
	jack->show();
	system("pause");
	delete jack;
	return 0;
}

 

类只是一个模板(Template),编译后不占用内存空间,所以在定义类时不能对成员变量进行初始化,因为没有地方存储数据。只有在创建对象以后才会给成员变量分配内存,这个时候就可以赋值了。

类可以理解为一种新的数据类型,该数据类型的名称是 Student 。与 char、int、float 等基本数据类型不同的是,Student 是一种复杂数据类型,可以包含基本类型,而且还有很多基本类型中没有的特性。

 

创建对象

有了 Student 类后,就可以通过它来创建对象了,例如:

Student liLei;  //创建对象
Student是类名,liLei是对象名。这和使用基本类型定义变量的形式类似:
int a;  //定义整型变量

从这个角度考虑,我们可以把 Student 看做一种新的数据类型,把 liLei 看做一个变量。


在创建对象时,class 关键字可要可不要,但是出于习惯我们通常会省略掉 class 关键字,例如:

class Student LiLei;  //正确
Student LiLei;  //同样正确
除了创建单个对象,还可以创建对象数组:
Student allStu[100];
该语句创建了一个 allStu 数组,它拥有100个元素,每个元素都是 Student 类型的对象。

 

访问类的成员

创建对象以后,可以使用点号.来访问成员变量和成员函数,这和通过结构体变量来访问它的成员类似。

 

使用对象指针

C语言中经典的指针在 C++ 中仍然广泛使用,尤其是指向对象的指针,没有它就不能实现某些功能。

创建对象 stu 在上分配内存,需要使用&获取它的地址,例如:
Student stu;
Student *pStu = &stu;
pStu 是一个指针,它指向 Student 类型的数据,也就是通过 Student 创建出来的对象。


当然,你也可以在上创建对象,这个时候就需要使用前面讲到的new关键字,例如:

Student *pStu = new Student;

在栈上创建出来的对象都有一个名字,比如 stu,使用指针指向它不是必须的。但是通过 new 创建出来的对象就不一样了,它在堆上分配内存,没有名字,只能得到一个指向它的指针,所以必须使用一个指针变量来接收这个指针,否则以后再也无法找到这个对象了,更没有办法使用它。也就是说,使用 new 在堆上创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。

栈内存是程序自动管理的,不能使用 delete 删除在栈上创建的对象;堆内存由程序员管理,对象使用完毕后可以通过 delete 删除。在实际开发中,new 和 delete 往往成对出现,以保证及时删除不再使用的对象,防止无用内存堆积。

有了对象指针后,可以通过箭头->来访问对象的成员变量和成员函数,这和通过结构体指针来访问它的成员类似,请看下面的示例:

pStu -> name = "小明";
pStu -> age = 15;
pStu -> score = 92.5f;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值