C++类和对象基础详解

目录
  • 一、类和对象的基本概念
    • 结构化程序设计
    • 面向对象的程序设计
    • 使用类的成员变量和成员函数
  • 二、类和对象基础 类成员的可访问范围
    • 构造函数(constructor)
      • 基本概念
    • 拷贝(复制)构造函数
    • 类型转换构造函数
    • 析构函数(destructors)
  • 总结

一、类和对象的基本概念

结构化程序设计

C语言使用结构化程序设计:

程序 = 数据结构 + 算法

程序由全局变量以及众多相互调用的函数组成 。算法以函数的形式实现,用于对数据结构进行操作。

结构化程序设计的不足

1.结构化程序设计中,函数和其所操作的数据结构,没有直观的联系。

2.随着程序规模的增加,程序逐渐难以理解,很难一下子看出来:

某个数据结构到底有哪些函数可以对它进行操作?

某个函数到底是用来操作哪些数据结构的?

任何两个函数之间存在怎样的调用关系?

3.结构化程序设计没有“封装”和“隐藏”的概念。 要访问某个数据结构中的某个变量,就可以直接访问,那么当该变量的定义有改动的时候,就要把所有访问该变量的语句找出来修改,十分不利于程序的维护、扩充。

4.难以查错,当某个数据结构的值不正确时,难以找出到底是那个函数导致的。

5.重用:在编写某个程序时,发现其需要的某项功 能,在现有的某个程序里已经有了相同或类似的 实现,那么自然希望能够将那部分代码抽取出来, 在新程序中使用。

6.在结构化程序设计中,随着程序规模的增大,由 于程序大量函数、变量之间的关系错综复杂,要抽取这部分代码,会变得十分困难。

总之,结构化的程序,在规模庞大时,会变得难以理解,难以扩充(增加新功能),难以查错,难以重用。

  • 如何更高效地实现函数的复用?
  • 如何更清晰的实现变量和函数的关系?使得程序 更清晰更易于修改和维护。

面向对象的程序设计

面向对象的程序 = 类 + 类 + …+ 类

面向对象的程序设计方法:

将某类客观事物共同特点(属性)归纳出来,形成一个数据 结构(可以用多个变量描述事物的属性);

将这类事物所能进行的行为也归纳出来,形成一个个函数, 这些函数可以用来操作数据结构(这一步叫“抽象”)。

然后,通过某种语法形式,将数据结构和操作该数据结构的函 数“捆绑”在一起,形成一个“类”,从而使得数据结构和操作该数据结构的算法呈现出显而易见的紧密关系,这就是“封装”。

面向对象的程序设计具有“抽象”,“封装”“继承”“多态” 四个基本特点。

例如:

将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一个“矩形类”。

长、宽变量成为该“矩形类”的“成员变量”,三个函数成为该类的“成员函数” 。 成员变量和成员函数统称为类的成员。

class CRectangle
{
public:
	int w, h;
	int Area() {
		return w * h;
	}
	int Perimeter(){
		return 2 * ( w + h);
	}
	void Init( int w_,int h_ ) {
		w = w_; h = h_;
	}
}; // 必须有分号

通过类,可以定义变量。类定义出来的变量,也称为类的实例,就是我们所说的“对象” 。

C++中,类的名字就是用户自定义的类型的名字。可以象使用基本类型那样来使用它。CRectangle 就是一种用户自定义的类型。

int main( )
{
	int w,h;
	CRectangle r; //r 是一个对象
	cin >> w >> h;
	r.Init( w,h);
	cout << r.Area() << endl
	<<r.Perimeter();
	return 0;
}

对象的内存分配

类的内存分配与结构体分配内存相同,类的成员函数所占内存不属于类。

每个对象各有自己的存储空间。一个对象的某个成员变量被改变了,不会影响到另一个对象。

和结构变量一样,对象之间可以用 “=”进行赋值,但是不能用 “==”,“!=”,“>”,“<”“>=”“<=”进行比较,除非这些运算符经过了“重载”(第四篇讲)。

使用类的成员变量和成员函数

用法1:对象名.成员名

CRectangle r1,r2;
r1.w = 5;
r2.Init(5,4);

用法2: 指针->成员名

CRectangle r1,r2;
CRectangle * p1 = & r1;
CRectangle * p2 = & r2;
p1->w = 5;
p2->Init(5,4); //Init作用在p2指向的对象上

用法3:引用名.成员名

CRectangle r2;
CRectangle & rr = r2;
rr.w = 5;
rr.Init(5,4); //rr 的值变了,r2 的值也变

二、类和对象基础 类成员的可访问范围

在类的定义中,用下列访问范围关键字来说明类成员

可被访问的范围:

private: 私有成员,只能在成员函数内访问
public : 公有成员,可以在任何地方访问
protected: 保护成员,后面再说,暂时理解和私有成员类似

以上三种关键字出现的次数和先后次序都没有限制。

代码如下(示例):

class className {
private:
	私有属性和函数
public:
	公有属性和函数
protected:
	保护属性和函数
};

如过某个成员前面没有上述关键字,则缺省地被认为是私有成员。

class Man {
	int nAge; // 私有成员
	char szName[20]; // 私有成员
public:
	void SetName(char * szName){
	strcpy( Man::szName,szName);
	}
};

在类的成员函数内部,能够访问:

  • 当前对象的全部属性、函数;
  • 同类其它对象的全部属性、函数。

在类的成员函数以外的地方,只能够访问该类对象的公有成员。

成员函数在类内声明,一般在类外实现(也可类内声明并实现),需要加上“类名::”。

class CEmployee {
private:
	char szName[30]; // 名字
public :
	int salary; // 工资
	void setName(char * name);
	void getName(char * name);
	void averageSalary(CEmployee e1,CEmployee e2);
};
void CEmployee::setName( char * name) {
	strcpy( szName, name); //ok
}
void CEmployee::getName( char * name) {
	strcpy( name,szName); //ok
}
void CEmployee::averageSalary(CEmployee e1,CEmployee e2){
	cout << e1.szName; //ok ,访问同类其他对象私有成员
	salary = (e1.salary + e2.salary )/2;
	}
int main()
{
	CEmployee e;
	strcpy(e.szName,"Tom1234567889"); // 编译错,不能访问私有成员
	e.setName( "Tom"); // ok
	e.salary = 5000; //ok
	return 0;
}

设置私有成员的机制,叫“隐藏”

  • “隐藏”的目的是强制对成员变量的访问一定要通过成员函数 进行,那么后成员变量的类型等属性修改后,只需要更改成员函数即可。否则,所有直接访问成员变量的语句都需要修改。

同样我们也可以用struct定义类

struct CEmployee {
	char szName[30]; // 公有!!
public :
	int salary; // 工资
	void setName(char * name);
	void getName(char * name);
	void averageSalary(CEmployee
	e1,CEmployee e2);
};

和用"class"的唯一区别,就是未说明是公有,还是私有的成员。

成员函数的重载及参数缺省

  • 成员函数也可以重载
  • 成员函数可以带缺省参数。

注:使用缺省参数要注意避免有函数重载时的二义性

构造函数(constructor)

基本概念

成员函数的一种
名字与类名相同,可以有参数,不能有返回值(void也不行)
作用是对对象进行初始化,如给成员变量赋初值
如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数
默认构造函数无参数,不做任何操作。

  • 如果定义了构造函数,则编译器不生成默认的无参数的构造函数
  • 对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数
  • 一个类可以有多个构造函数

为什么需要构造函数

构造函数执行必要的初始化工作,有了构造函数,就不 必专门再写初始化函数,也不用担心忘记调用初始化函数。

有时对象没被初始化就使用,会导致程序出错。对象不初始化是件很糟糕的事。

代码如下(示例):

class Complex {
private :
double real, imag;
public:
	Complex( double r, double i = 0);
};
Complex::Complex( double r, double i) {
	real = r; imag = i;
}
int main()
{
	Complex c1; // error, 缺少构造函数的参数
	Complex * pc = new Complex; // error, 没有参数
	Complex c1(2); // OK
	Complex c1(2,4), c2(3,5);
	Complex * pc = new Complex(3,4);
	return 0;
}

同样的构造函数也可以根据需求重载。

构造函数最好是public的,private构造函数不能直接用来初始化对象

构造函数在数组中的使用

class Test {
public:
	Test( int n) { } //(1)
	Test( int n, int m) { } //(2)
	Test() { } //(3)
};
Test array1[3] = { 1, Test(1,2) };// 三个元素分别用(1),(2),(3)初始化
Test * pArray[3] = { new Test(4), new Test(1,2) };//两个元素分别用(1),(2) 初始化

拷贝(复制)构造函数

  • 只有一个参数,即对同类对象的引用。 形如 X::X( X& )或X::X(const X &), 二者选一 后者能以常量对象作为参数
  • 如果没有定义拷贝构造函数,那么编译器生成默认拷贝构造函数。默认的拷贝构造函数完成复制功能。

如果定义的自己的拷贝构造函数,则默认的拷贝构造函数不存在。

class Complex {
public :
	double real,imag;
	Complex(){ }
	Complex( const Complex & c ) {
	real = c.real;
	imag = c.imag;
	cout << “Copy Constructor called”;
	}
};
Complex c1;
Complex c2(c1);//调用自己定义的拷贝构造函数,输出 Copy Constructor called

拷贝构造函数起作用的三种情况

当用一个对象去初始化同类的另一个对象时。

Complex c2(c1);
Complex c2 = c1; //初始化语句,非赋值语句

如果某函数有一个参数是类 A 的对象, 那么该函数被调用时,类A的拷贝构造函数将被调用。

class A
{
public:
	A() { };
	A( A & a) {
		cout << "Copy constructor called" <<endl;
	}
};
void Func(A a1){ }
int main(){
	A a2;
	Func(a2);
	return 0;
}

程序输出结果为: Copy constructor called

如果函数的返回值是类A的对象时,则函数返回时, A的拷贝构造函数被调用:

class A
{
public:
	int v;
	A(int n) { v = n; };
	A( const A & a) {
		v = a.v;
		cout << "Copy constructor called" <<endl;
	}
};
A Func() {
A b(4);
	return b;
}
int main() {
	cout << Func().v << endl; return 0;
}

输出结果:

Copy constructor called
4

注意:对象间赋值并不导致拷贝构造函数被调用

类型转换构造函数

  • 定义转换构造函数的目的是实现类型的自动转换。
  • 只有一个参数,而且不是拷贝构造函数的构造函数,一般就可以看作是转换构造函数。
  • 当需要的时候,编译系统会自动调用转换构造函数,建立 一个无名的临时对象(或临时变量)。
//Comlex是一个类
Complex( int i) {// 类型转换构造函数
	cout << "IntConstructor called" << endl;
	real = i; imag = 0;
}

析构函数(destructors)

名字与类名相同,在前面加‘~', 没有参数和返回值,一个类最多只能有一个析构函数。

析构函数对象消亡时即自动被调用。可以定义析构函数来在 对象消亡前做善后工作,比如释放分配的空间等

如果定义类时没写析构函数,则编译器生成缺省析构函数。 缺省析构函数什么也不做。

如果定义了析构函数,则编译器不生成缺省析构函数。

class String{
private :
char * p;
public:
	String () {
	p = new char[10];
	}
	~ String () ;
};
String::~ String()
{
	delete [] p;
}

对象数组生命期结束时,对象数组的每个元素的析构函数都会被调用。
delete 运算导致析构函数调用。

Ctest * pTest;
pTest = new Ctest; // 构造函数调用
delete pTest; // 析构函数调用

pTest = new Ctest[3]; // 构造函数调用3次 次
delete [] pTest; // 析构函数调用3次

一旦有对象生成调用构造函数
一旦有对象消亡调用析构函数

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C++11/14 线程调用类对象和线程传参的方法

    线程调用类对象 在前面的示例中,我们为线程任务使用了通常的函数.实际上,我们可以使用任何可调用对象或者lambda函数,如下调用类对象的例子: #include <iostream> #include <thread> class MyFunctor { public: void operator()() { std::cout << "functor\n"; } }; int main() { MyFunctor fnctor; std::thre

  • C++中对象&类的深入理解

    什么是对象 任何事物都是一个对象, 也就是传说中的万物皆为对象. 对象的组成: 数据: 描述对象的属性 函数: 描述对象的行为, 根据外界的信息进行相应操作的代码 具有相同的属性和行为的对象抽象为类 (class) 类是对象的抽象 对象则是类的特例 面向过程 vs 面向对象 面向过程 面向过程的设计: 围绕功能, 用一个函数实现一个功能 程序 = 算法 +数据结构 算法和数据结构两者互相独立, 分开设计 面向对象 面向对象的设计: 把算法和数据封装在一个对象中 设计所需要的歌者类和对象 向有关对

  • c++ 类和对象总结

    话不多说,我们直接进入主题: 对象:客观世界里的一切事物都可以看作是一个对象,每一个对象应当具有属性(静态特征,比如一个班级,一个专业,一个教室)和行为(动态特征,例如:学习,开会,体育比赛等)两个要素. 对象是由一组属性和一组行为构成的. 类(class):就是对象的类型,代表了某一批对象的共同特性和特征.类是对象的抽象,而对象是类的具体实例. 2.1 类的引入 在C语言中我们定义一个结构体是这样定义的: struct Student { int _age; char* _Gender; ch

  • C++ 类和对象基础篇

    类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量:创建对象的过程也叫类的实例化.每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数. 一.类的定义 一个简单的类的定义: class Student{ public: //成员变量 char *name; int age; float score; //成员函数 void say(){ cout<<name<<"的年龄是"<<age<<&qu

  • C++OOP对象和类的详细讲解

    目录 C++OOP对象和类 1.预备知识 2.抽象和类 2.1 数据抽象 2.2 类 2.3 接口 3.C++中的类和对象 3.1 C++类的定义 3.2 C++对象的定义 3.3 C++访问数据成员 3.4 C++类成员函数 总结 C++OOP对象和类 本文将从入门的角度介绍C++OOP中的类和对象,事实上C++的类和对象远远不止本文所介绍的内容. 不过如果你感兴趣,我会把其他的内容逐一通过文章的形式介绍给你. 1.预备知识 面向对象编程(OOP)是一种特殊的.设计程序的概念性方法,C++通过

  • C++类和对象基础详解

    目录 一.类和对象的基本概念 结构化程序设计 面向对象的程序设计 使用类的成员变量和成员函数 二.类和对象基础 类成员的可访问范围 构造函数(constructor) 基本概念 拷贝(复制)构造函数 类型转换构造函数 析构函数(destructors) 总结 一.类和对象的基本概念 结构化程序设计 C语言使用结构化程序设计: 程序 = 数据结构 + 算法 程序由全局变量以及众多相互调用的函数组成 .算法以函数的形式实现,用于对数据结构进行操作. 结构化程序设计的不足 1.结构化程序设计中,函数和

  • Java面向对象类和对象实例详解

    目录 1 - Java面向对象学习的三条主线 2 - 面向过程与面向对象 3 - 面向对象的三大特征 4 - 面向对象分析方法分析问题的思路和步骤 5 - 面向对象的思想概述 6 - 类和对象的创建-内存解析 7 - 类中属性的声明和使用(局部变量与成员变量的区别) 8 - 类中方法的声明和使用 9 - 对象数组的使用 10 - 理解 "万物皆对象" 11 - 匿名对象 12 - 方法的封装(数组常用算法的封装) 总结 1 - Java面向对象学习的三条主线 ①Java类即类的成员:属

  • python 类的基础详解与应用

    目录 类的定义 类对象 变量 类变量 局部变量 实例变量 私有变量 类的方法 特殊方法 继承 单继承 多继承 类的定义 # class是定义类的关键字,ClassName为类的名称 class ClassName: # 在这里写其他内容 pass class ClassName(object): # 在这里写其他内容 pass 这是一个最简单的类定义,在python3后类定义一般都会继承object类,不过不继承也没有多大的问题. 类对象 类对象也称实例 # 这就是创建了一个类对象(实例),a是

  • 把JSON数据格式转换为Python的类对象方法详解(两种方法)

    JOSN字符串转换为自定义类实例对象 有时候我们有这种需求就是把一个JSON字符串转换为一个具体的Python类的实例,比如你接收到这样一个JSON字符串如下: {"Name": "Tom", "Sex": "Male", "BloodType": "A", "Hobbies": ["篮球", "足球"]} 我需要把这个转换为具

  • Bootstrap学习笔记之进度条、媒体对象实例详解

    1.基础进度条 要写在<div class="progress"></div>里面. <div class="col-md-6"> <div class="progress"> <div class="progress-bar" style="width:30%;"></div> </div> </div> 2.

  • java对象拷贝详解及实例

    java对象拷贝详解及实例 Java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的: @Test public void testassign(){ Person p1=new Person(); p1.setAge(31); p1.setName("Peter"); Person p2=p1; System.out.println(p1==p2);//true } 如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态,

  • Java 基础详解(泛型、集合、IO、反射)

    计划把 Java 基础的有些部分再次看一遍,巩固一下,下面以及以后就会分享自己再次学习的一点笔记!不是有关标题的所有知识点,只是自己觉得模糊的一些知识点. 1.对于泛型类而言,你若没有指明其类型,默认为Object: 2.在继承泛型类以及接口的时候可以指明泛型的类型,也可以不指明: 3.泛型也数据库中的应用: 写一个 DAO 类对数据库中的数据进行增删改查其类型声明为 <T> .每张表对应一个类,对应每一张表实现一个类继承该 DAO 类并指明 DAO 泛型为该数据表对应的类,再实现一个与该表匹

  • js对象实例详解(JavaScript对象深度剖析,深度理解js对象)

    这算是酝酿很久的一篇文章了. JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕. 平时发的文章基本都是开发中遇到的问题和对最佳解决方案的探讨,终于忍不住要写一篇基础概念类的文章了. 本文探讨以下问题,在座的朋友各取所需,欢迎批评指正: 1.创建对象 2.__proto__与prototype 3.继承与原型链 4.对象的深度克隆 5.一些Object的方法与需要注意的点 6.ES6新增特性 下面反复提到实例对象和原型对象,通过构造函数 new

  • c++ 智能指针基础详解

    简介 在现代 C++ 编程中,标准库包含了智能指针(Smart pointers). 智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe)的. 智能指针的使用 智能指针定义在头文件 memory 里的命名空间 std 中.它对于资源获取即初始化(RAII, Resource Acquisition Is Initialization) 编程理念至关重要.该理念的目的是保证对象初始化的时候也是资源获取的时候,从而使对象的所有资源在单行代码中创建

  • Java基础详解之包装类的装箱拆箱

    一.包装类 概念: Java提供了两个类型系统,基本数据类型和引用数据类型,使用基本数据类型在于效率,然而很多情况下回创建对象使用,因为对象能做更多的功能. 所以可以使用一个类,把基本数据类型包装起来,在类中定义一些方法,这就叫做包装类.我们可以用这种方法来操作这些数据类型 基本类型 对应包装类(位于java.lang中) byte Byte short Short int Integer long Long float Float double Double char Character bo

随机推荐