您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

C++的逐层抽象:从结构体到类、模板

时间:2021-10-18 12:05:37  来源:  作者:小智雅汇

C++在C的面向过程概念的基础上提供了面向对象和模板(泛型编程)的语法功能。

下面以一个简单实例(动态数组的简单封装,包括下标的值可以是任意正数值,并提供边界检查)来说明C++是如何实现其逐层抽象的。

1 用结构体实现

// array.h
// array库的接口
#ifndef _array_h
#define _array_h

// 可指定下标范围的数组的存储
struct DoubleArray{
    int low;  
    int high;
    double *storage;
};

// 根据low和high为数组分配空间。分配成功,返回值为true,否则返回值为false
bool initialize(DoubleArray &arr, int low, int high);

// 设置数组元素的值
// 返回值为true表示操作正常,返回值为false表示下标越界
bool insert(const DoubleArray &arr, int index, double value);

// 取数组元素的值
// 返回值为true表示操作正常,返回值为false表示下标越界
bool fatch(const DoubleArray &arr, int index, double &value);

// 回收数组空间
void cleanup(const DoubleArray &arr);

#endif
// array.cpp
// array库的实现
#include "array.h"
#include <IOStream>
using namespace std;

// 根据low和high为数组分配空间。分配成功,返回值为true,否则返回值为false
bool initialize(DoubleArray &arr, int low, int high)
{
    arr.low = low; 
    arr.high = high;
    arr.storage = new double [high - low + 1];
    if (arr.storage == NULL) 
        return false; 
    else 
        return true;
}

// 设置数组元素的值
// 返回值为true表示操作正常,返回值为false表示下标越界
bool insert(const DoubleArray &arr, int index, double value)
{
    if (index < arr.low || index > arr.high) 
        return false;
    arr.storage[index - arr.low] = value;
    return true;
}

// 取数组元素的值
// 返回值为true表示操作正常,返回值为false表示下标越界
bool fatch(const DoubleArray &arr, int index, double &value)
{
    if (index < arr.low || index > arr.high) 
        return false;
    value = arr.storage[index - arr.low] ;
    return true;
}

// 回收数组空间
void cleanup(const DoubleArray &arr)
{  
    if (arr.storage)  
        delete [] arr.storage; 
}

// arrayApp.cpp
// array库的应用示例
#include <iostream>
using namespace std;
#include "array.h"

int main() 
{
	DoubleArray array; 					// DoubleArray是array库中定义的结构体类型
    double value;
	int low, high, i;
	
    //输入数组的下标范围
	cout <<"请输入数组的下标范围:";
	cin >> low >> high;
	
	//初始化数组array,下标范围为20到30
	if (!initialize(array, low, high)) { 
        cout <<"空间分配失败" ; return 1;
    }
	
    for (i = low; i <= high; ++i) { 		// 数组元素的输入
		cout <<"请输入第"<< i <<"个元素:";
		cin >> value;
		insert(array, i, value); 		     // 将value存入数组array的第i个元素
    }
	
    while (true) { 					         // 读取第i个元素
		cout <<"请输入要读取的元素序号(0表示结束):";
		cin >> i;
		if (i == 0) 
            break;
		if (fatch(array, i, value)) 
            cout << value << endl;
		else 
            cout <<"下标越界n";
    }
	
    cleanup(array);					        //回收存储数组元素的空间
	
    return 0;
}

相关的函数全部有一个结构体函数参数,来操作结构体。通过头文件包括结构体和函数声明来实现模块化并提供接口,实现一个较松散的数据与函数的结合。

而类则不同,类除了提供数据聚合的结构体功能以外,还可以将函数封装进去,通过类名实现名字空间,并为成员函数提供一个隐含的指向类对象的this指针,使其成员函数能够访问并操作数据成员。

2 用一个不完整的类来模拟结构体实现

// array.h
// 改进后的array接口
#ifndef _array_h
#define _array_h

struct DoubleArray{
    int low;  
    int high;
    double *storage;
    
    // 根据low和high为数组分配空间。分配成功,返回值为true,否则返回值为false
    bool initialize(int lh, int rh);
    
    // 设置数组元素的值
    // 返回值为true表示操作正常,返回值为false表示下标越界
    bool insert(int index, double value);
    
    // 取数组元素的值
    // 返回值为true表示操作正常,返回值为false表示下标越界
    bool fatch(int index, double &value);
    
    // 回收数组空间
    void cleanup();
};

#endif
// array.cpp
// 改进后的array库的实现
#include "array.h"
#include <iostream>
using namespace std;

// 根据low和high为数组分配空间。分配成功,返回值为true,否则返回值为false
bool DoubleArray::initialize(int lh, int rh)
{
    low = lh;
    high = rh;
    storage = new double [high - low + 1];
    if (storage == NULL) 
        return false; 
    else return true;
}

// 设置数组元素的值
// 返回值为true表示操作正常,返回值为false表示下标越界
bool DoubleArray::insert(int index, double value)
{   
    if (index < low || index > high) 
        return false;
    storage[index - low] = value;
    return true;
}

// 取数组元素的值
// 返回值为true表示操作正常,返回值为false表示下标越界
bool DoubleArray::fatch(int index, double &value)
{
    if (index < low || index > high) 
        return false;
    value = storage[index - low] ;
    return true;
}

//回收数组空间
void DoubleArray::cleanup()
{   
    if (storage) 
        delete [] storage; 
}

// arrayapp.cpp
// 改进后的array库的应用示例
#include <iostream>
using namespace std;
#include "array.h"

int main()
{
    DoubleArray array;
    double value;
    int low, high, i;
    
    //输入数组的下标范围
    cout <<"请输入数组的下标范围:";
    cin >> low >> high;
    
    if (!array.initialize(low, high)) {         // 为array申请存储数组元素的空间
        cout <<"空间分配失败" ; return 1;
    }
    
    //数组array的初始化
    for (i = low; i <= high; ++i) {             //数组元素的输入
        cout <<"请输入第"<< i <<"个元素:";
        cin >> value;
        array.insert(i, value);
    }
    
    while (true) {                              //数组元素的访问
        cout <<"请输入要访问的元素序号(0表示结束):";
        cin >> i;
        if (i == 0) 
            break;
        if (array.fatch(i, value)) 
            cout << value << endl;
        else 
            cout <<"下标越界n";
    }
    
    array.cleanup();                //归还存储数组元素的空间
    
    return 0;
}

3 用定义较为完整的类来实现

类还实现了资源获取即初始化(RAII, Resource Acquisition Is Initialization)以及自动实现资源析构的功能,以及对运算符的重载等功能。

// DoubleArray.h
// DoubleArray类的定义
#ifndef _array_h
#define _array_h

#include <iostream.h>

class DoubleArray{
    friend ostream &operator<<(ostream &os, const DoubleArray &obj);
    friend istream &operator>>(istream &is, DoubleArray &obj);
    friend bool operator==(const DoubleArray &obj1, const DoubleArray &obj2);
    
private:
    int low;  
    int high;
    double *storage;
    
public:
    // 构造函数根据low和high为数组分配空间
    DoubleArray(int lh = 0, int rh = 0):low(lh), high(rh)
    {
        storage = new double [high - low + 1]; 
    }	
    
    // 复制构造函数
    DoubleArray(const DoubleArray &arr);   
    
    // 赋值运算符重载函数
    DoubleArray &operator=(const DoubleArray &right);
    
    // 下标运算符重载函数
    double & operator[](int index);              // 作为左值
    const double & operator[](int index) const;  // 作为右值
    
    // 取数组的一部分形成一个新的数组
    DoubleArray operator()(int start, int end, int lh);
    
    // 析构函数
    ~DoubleArray() { 
        if (storage) 
            delete [] storage; 
    }
};
#endif

// DoubleArray.cpp
// DoubleArray类的实现
#include <cassert>
#include "DoubleArray.h"

DoubleArray::DoubleArray(const DoubleArray &arr)
{
    low = arr.low; 
    high = arr.high;
    storage = new double [high - low + 1];
    for (int i = 0; i < high -low + 1; ++i)  
        storage[i] = arr.storage[i];
}

DoubleArray &DoubleArray::operator=(const DoubleArray & a)
{
    if (this == &a)                         // 防止自己复制自己
        return *this;
    delete [] storage;                      // 归还空间
    
    low = a.low;  high = a.high;
    storage = new double[high - low + 1];   // 根据新的数组大小重新申请空间
    for (int i=0; i <= high - low; ++i) 
        storage[i] = a.storage[i];          // 复制数组元素
    
    return *this;
}

double & DoubleArray::operator[](int index)
{ 
    assert(index >= low && index <= high); 
    return storage[index - low];
}

const double & DoubleArray::operator[](int index) const
{
    assert(index >= low && index <= high); 
    return storage[index - low];
}

ostream &operator<<(ostream &os, const DoubleArray &obj)
{
    os <<"数组内容为:n";
    for (int i=obj.low; i<=obj.high; ++i) 
        os << obj[i] << 't';
    os << endl;
    
    return os;
}

istream &operator>>(istream &is, DoubleArray &obj)
{
    cout <<"请输入数组元素["<< obj.low <<", "<< obj.high <<"]:n";
    for (int i=obj.low; i<=obj.high ; ++i)   
        is >> obj[i] ;
    
    return is;
}

bool operator==(const DoubleArray &obj1, const DoubleArray &obj2)
{
    if (obj1.low != obj2.low || obj1.high != obj2.high) 
        return false;
    for (int i = obj1.low; i<=obj1.high; ++i) 
        if (obj1[i] != obj2[i]) 
            return false;
    return true;
}

DoubleArray DoubleArray::operator()(int start, int end, int lh)
{
    assert (start <= end && start >= low && end <= high );  // 判断范围是否正确
    
    DoubleArray tmp(lh, lh + end - start);                  // 为取出的数组准备空间
    for (int i = 0; i < end - start + 1; ++i) 
        tmp.storage[i] = storage[start + i - low];
    
    return tmp;
}
// DoubleArrayApp.cpp
// DoubleArray类的使用
#include "DoubleArray.h"

int main()
{
    DoubleArray array1(20,30), array2;
    
    cin >> array1;                      // 利用流提取运算符重载输入array1
    cout <<"array1 "; cout << array1;   // 利用流插入运算符重载输出array1
    
    array2 = array1;                    // 利用赋值运算符重载将array1赋给array2
    
    cout <<"执行 array2 = array1, array2 ";
    cout << array2;
    
    // 利用==重载比较array1和array2
    cout <<"array1 == array2 是 "<< ((array1 == array2)  ? "true" : "false") << endl; 
    
    array2[25] = 0;                      // 利用下标运算符重载为array2的元素赋值
    
    cout <<"执行array[25] = 0后, array1 == array2 是 "
        << ((array1 == array2)  ? "true" : "false") << endl; 
    
    array2 = array1(22, 25, 2);
    cout <<"执行array2 = array1(22, 25, 2)后, array2 的值为: "<< array2;	
    
    while(1);
    return 0;
}

4 用模板实现泛型

强类型一定程度上实现了类型在编译期的检查功能,而模板却可以让类并不囿于过于具体的类型:

// array.h
#include <iostream.h>
// 类模板的友元:重载输入运算符
template<class T> 
    class Array;	                                      // 类模板Array的声明
template<class T> 
    ostream &operator<<(ostream &os, const Array<T>&obj); // 输出重载声明

template <class T>
class Array {
    friend ostream &operator<<(ostream &os, const Array<T>&obj);
    
private:
    int low;  
    int high;
    T *storage;
    
public:
    // 根据low和high为数组分配空间。分配成功,返回值为true,否则返回值为false
    Array(int lh = 0, int rh = 0):low(lh),high(rh)
    { 
        storage = new T [high - low + 1]; 
    }
    
    // 复制构造函数
    Array(const Array &arr);
    
    // 赋值运算符重载函数
    Array &operator=(const Array & a);
    
    // 下标运算符重载函数
    T & operator[](int index);
    const T & operator[](int index) const;  // 作为右值
    
    // 回收数组空间
    ~Array(){
        if(storage)
            delete [] storage; 
    }  
};

template <class T>
T & Array<T>::operator[](int index)
{
    if (index < low || index > high)
    {
        cout <<"下标越界"; 
        return;
    }
    return storage[index - low];
}

template <class T>
const T & Array<T>::operator[](int index) const
{
    if (index < low || index > high)
    {
        cout <<"下标越界"; 
        return;
    }
    return storage[index - low];
}

// 类模板的友元:重载输出运算符
template<class T>
ostream &operator<<(ostream &os, const Array<T>&obj)
{
    os << endl;
    for (int i=0; i < obj.high - obj.low + 1; ++i) 
        os << obj.storage[i] << 't';
    return os;
}

template<class T>
Array<T>::Array(const Array<T> &arr)
{
    low = arr.low; 
    high = arr.high;
    storage = new T [high - low + 1];
    for (int i = 0; i < high -low + 1; ++i)  
        storage[i] = arr.storage[i];
}

template<class T>
Array<T> &Array<T>::operator=(const Array<T> & a)
{
    if (this == &a)                         // 防止自己复制自己
        return *this;
    
    delete [] storage;                      // 归还空间
    
    low = a.low;  high = a.high;
    storage = new T[high - low + 1];        // 根据新的数组大小重新申请空间
    for (int i=0; i <= high - low; ++i) 
        storage[i] = a.storage[i];          // 复制数组元素
    
    return *this;
}

// main.cpp

#include <iostream.h>
#include "array.h"

int main()
{
    int start, end;
    cout<<"请输入数组的下标和上标,如1 9:";
    cin>>start>>end;
    Array<double> arr(start,end), arr2;
    for(int i = start ; i<= end-start+1 ; i++)
        arr[i] = i*0.9;
    arr[8] = 8.8;
    arr2 = arr;
    cout<<arr2<<endl;

    while(1);
    return 0;
}

-End-



Tags:C++   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
一、编程语言1.根据熟悉的语言,谈谈两种语言的区别?主要浅谈下C/C++和PHP语言的区别:1)PHP弱类型语言,一种脚本语言,对数据的类型不要求过多,较多的应用于Web应用开发,现在好多互...【详细内容】
2021-12-15  Tags: C++  点击:(17)  评论:(0)  加入收藏
函数调用约定(Calling Convention),是一个重要的基础概念,用来规定调用者和被调用者是如何传递参数的,既调用者如何将参数按照什么样的规范传递给被调用者。在参数传递中,有两个很...【详细内容】
2021-11-30  Tags: C++  点击:(19)  评论:(0)  加入收藏
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  Tags: C++  点击:(37)  评论:(0)  加入收藏
C++编程中,你是否有为 我到底该写个struct还是class 而苦恼过?如果你到现在还不知道该如何选择,那么请求继续阅读,下文或许能给你些建议。问题的产生C++语言继承了 C语言的 stru...【详细内容】
2021-10-18  Tags: C++  点击:(61)  评论:(0)  加入收藏
C++在C的面向过程概念的基础上提供了面向对象和模板(泛型编程)的语法功能。下面以一个简单实例(动态数组的简单封装,包括下标的值可以是任意正数值,并提供边界检查)来说明C++是如...【详细内容】
2021-10-18  Tags: C++  点击:(49)  评论:(0)  加入收藏
0 前言Hello,大家好,欢迎来到『自由技艺』的 C++ 系列专题。代码重用,尽可能避免冗余代码是程序员的一项必备技能,今天就来给大家介绍其中一种:函数装饰器。在设计模式中,与它对应...【详细内容】
2021-09-28  Tags: C++  点击:(75)  评论:(0)  加入收藏
今天我们就来聊一聊 C++ 中的异常机制吧。在学校期间,我们很少会用得上异常机制。然而,工作之后,很多时候却不得不引入异常机制。因为一般情况下,使用函数的返回值来确定函数的...【详细内容】
2021-09-26  Tags: C++  点击:(181)  评论:(0)  加入收藏
一、内存泄漏(memory leak)内存泄漏(memory leak)是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存...【详细内容】
2021-09-03  Tags: C++  点击:(104)  评论:(0)  加入收藏
stack容器#include <iostream>using namespace std;#include <stack>//容器头文件void test(){stack<int>p;p.push(100);p.push(1000);p.push(100);while(!p.empty()){cout<...【详细内容】
2021-08-17  Tags: C++  点击:(81)  评论:(0)  加入收藏
stl 常用遍历算法(for_each transform)示例代码(结论在结尾!!!!)#include<iostream>using namespace std;#include"vector"#include"map"#include"string"#include"list"#in...【详细内容】
2021-08-13  Tags: C++  点击:(89)  评论:(0)  加入收藏
▌简易百科推荐
一、简介很多时候我们都需要用到一些验证的方法,有时候需要用正则表达式校验数据时,往往需要到网上找很久,结果找到的还不是很符合自己想要的。所以我把自己整理的校验帮助类分...【详细内容】
2021-12-27  中年农码工    Tags:C#   点击:(0)  评论:(0)  加入收藏
引言在学习C语言或者其他编程语言的时候,我们编写的一个程序代码,基本都是在屏幕上打印出 hello world ,开始步入编程世(深)界(坑)的。C 语言版本的 hello world 代码:#include <std...【详细内容】
2021-12-21  一起学嵌入式    Tags:C 语言   点击:(10)  评论:(0)  加入收藏
读取SQLite数据库,就是读取一个路径\\192.168.100.**\position\db.sqlite下的文件<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0"/...【详细内容】
2021-12-16  今朝我的奋斗    Tags:c#   点击:(21)  评论:(0)  加入收藏
什么是shellshell是c语言编写的程序,它在用户和操作系统之间架起了一座桥梁,用户可以通过这个桥梁访问操作系统内核服务。 它既是一种命令语言,同时也是一种程序设计语言,你可以...【详细内容】
2021-12-16  梦回故里归来    Tags:shell脚本   点击:(16)  评论:(0)  加入收藏
一、编程语言1.根据熟悉的语言,谈谈两种语言的区别?主要浅谈下C/C++和PHP语言的区别:1)PHP弱类型语言,一种脚本语言,对数据的类型不要求过多,较多的应用于Web应用开发,现在好多互...【详细内容】
2021-12-15  linux上的码农    Tags:c/c++   点击:(17)  评论:(0)  加入收藏
1.字符串数组+初始化char s1[]="array"; //字符数组char s2[6]="array"; //数组长度=字符串长度+1,因为字符串末尾会自动添&lsquo;\0&lsquo;printf("%s,%c\n",s1,s2[2]);...【详细内容】
2021-12-08  灯-灯灯    Tags:C语言   点击:(46)  评论:(0)  加入收藏
函数调用约定(Calling Convention),是一个重要的基础概念,用来规定调用者和被调用者是如何传递参数的,既调用者如何将参数按照什么样的规范传递给被调用者。在参数传递中,有两个很...【详细内容】
2021-11-30  小智雅汇    Tags:函数   点击:(19)  评论:(0)  加入收藏
一、问题提出问题:把m个苹果放入n个盘子中,允许有的盘子为空,共有多少种方法?注:5,1,1和1 5 1属同一种方法m,n均小于10二、算法分析设f(m,n) 为m个苹果,n个盘子的放法数目,则先对...【详细内容】
2021-11-17  C语言编程    Tags:C语言   点击:(46)  评论:(0)  加入收藏
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  深度Linux    Tags:C++   点击:(37)  评论:(0)  加入收藏
OpenCV(Open Source Computer Vision Library)是一个(开源免费)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android、ios等操作系统上,它轻量级而且高效---由一系列...【详细内容】
2021-11-11  zls315    Tags:C#   点击:(50)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条