`

C++/C高级数据类型

阅读更多

C语言中的构造数据类型如结构、联合、枚举等在C++中仍然有效。由于C++新增了一种类型名class,许多人错误地认为struct只能用来包装数据,或者class必须定义成员函数。

C++对C的结构、联合、枚举等进行了必要的改造和增强,本章比较分析了异同点,总结了使用要点,对于那些正在从C语言向C++语言过渡的程序员有较好的参考价值。
如果只能使用基本数据类型来编程,那将是一件痛苦的事情。C语言支持把基本数据类型组合起来形成更大的构造数据类型,这就是C语言的struct,有时也称为用户自定义数据类型(User Defined Type, UDT)。构造数据类型还可以嵌套(对象嵌入)和引用(对象关联),实际上,构造数据类型是一个递归的定义:
(1)由若干基本数据类型组合而成的类型是构造数据类型。
(2)由若干基本数据类型和构造数据类型组合而成的数据类型是构造数据类型。
(3)由若干构造数据类型组合而成的数据类型是构造数据类型。
语言本身的这种能力使我们能够定义非常复杂的数据结构,如树(tree)、链表(list)和映射(map)等。
关键字struct与class的困惑
C++语言对C语言的struct进行了改造,使其也可以像class那样支持成员函数的声明和定义,从而使struct变成真正的抽象数据类型(Abstract Data Type, ADT),这使得许多人对struct和class倍感困惑。
当语言支持某种特征时,是否使用这种特征则完全取决于程序员。因此,并不是说class支持成员函数的定义,我们就一定要在每一个class中都定义成员函数;也并不是说struct过去不支持成员函数定义,我们就非得用class完全取代struct。实际上就C++语言本身来讲,struct和class除了“默认的成员访问权限”这一点不同外,没有任何区别。

 

【提示8-1】:
在C++语言中,如果不特别指明,struct成员的默认访问限定符为public,而class成员的默认访问限定符为private。

 

因此,在C++程序中,只要你明确地声明每一个成员的访问权限,那么完全可以用struct取代class,也完全可以用class取代struct,见示例8-1。
 
 
示例8-1

 

struct SA
{
public :
const char * GetName( ) const;
private :
char *m_name ;
int m_height ;
int m_weight ;
};
class CA
{
public :
const char * GetName( ) const;
private :
char *m_name ;
int m_height ;
int m_weight ;
};

 

本例中SA和CA这两个类型在C++中没有任何不同。就像Lippman所说的那样,“在C++中,选择使用关键字struct还是class来定义UDT或ADT完全是一种观念上的差异,而关键字本身并没有代表这种差异”。
我们再看一看C++鼻祖Bjarne Stroustrup是如何说的:“带类的C和C语言几乎是‘代码兼容’的,并且也是连接兼容的。C的函数可以在带类的C程序中调用,带类的C函数也可以在C程序中调用;带类的C程序中的struct和C中的struct在两个语言里的布局都一致,所以可以在两个语言之间传递简单对象或组合对象。这种连接兼容性一直保持到C++中。”
C++仍然支持C风格的struct,并且还做了增强,主要是为了兼容遗留的C代码以使它们可以在新的C++环境下重新编译而继续“发挥余热”,可以让“过程式和结构化思想根深蒂固”的C程序员比较容易地过渡到面向对象的C++语言。关于这个问题更具哲学性的讨论请参考Lippman所著的《Inside The C++ Object Model》一书。

 

【建议8-1】:
为了不使程序产生混乱和妨碍理解,建议还使用struct定义简单的数据集合;而定义一些具有行为的ADT时最好采用class,如果采用struct似乎感觉不到面向对象的味道了。

 

使用struct
在C++环境中,我们把C风格的struct叫做POD(Plain Old Data)对象,从字面上你也可以知道它仅包含一些数据成员,这些数据成员可以是基本数据类型变量、任何类型的指针或引用、任何类型的数组及其他构造类型的对象等,见示例8-2。
示例8-2

 

【提示8-2】:
虽然把数组当作参数传递给函数的时候,数组将自动转换为指针,但是包装在struct/class中的数组,其内存空间则完全属于该struct/class的对象所有。如果把struct/class当作参数传递给函数时,默认为值传递,其中的数组将全部复制到函数堆栈中。例如:
void func (Student s)
{
     cout << sizeof (s) << endl ;    // 56
}
Student  s0 ;
func (s0) ;
因此,当你的UDT/ADT中包含数组成员的时候,最好使用指针或引用传递该类型的对象,并且一定要防止数组元素越界,否则它会覆盖后面的结构成员。

 

任何POD对象的初始化都可以使用memset()函数或者其他类似的内存初始化函数。假设s是Student的一个对象,用memset()初始化s的方法如下:
memset (&s, 0x00, sizeof (Student)) ;
C风格的构造类型对象也可以在定义的时候指定初始值。我们可以仅指定第一个成员的初值来初始化POD对象,后面的成员将全部自动初始化为0,就像数组的初始化一样。例如:
Student s = { 0 };
结构可以嵌套定义,也就是在一个结构的定义体内定义另一个结构,见示例8-3。
示例8-3

 

【提示8-3】:
构造类型虽然可以嵌套定义,但是对于嵌套定义的类型,其对象不一定存在包含关系,存在包含关系的对象类型也不一定是嵌套定义的。例如,上例中的_Name类型完全可以挪到Student定义的外面某处,而它们的对象之间的包含关系不会改变。当一个类型A只会在另一个类型B中被使用的时候,就可以把A定义在B的定义体内,这样可以减少暴露在外面的用户自定义类型的个数。

 

所谓对象之间的包含是指一个类型的对象充当了另一个类型定义的数据成员,从而也就充当了它的对象的成员,即两个对象之间存在has-a关系。但是要注意:一个对象不能自包含,无论是直接的还是间接的,因为编译器无法为它计算sizeof值,也就不知道该给这样的对象分配多少存储空间,见示例8-4。
示例8-4

 

struct A
{
int i ;
B   b ;
};
struct B
{
char ch ;
  a ;
};

 

假设A定义在B的前面,于是计算A的大小就需要知道B的大小,而计算B的大小又需要A的大小,……,于是陷入了“鸡生蛋还是蛋生鸡”的怪圈!这样的代码在编译的时候肯定通不过。
虽然对象不能自包含,但可以自引用,而且两个类型可以交叉引用,这种关系称为holds-a关系。因为任何类型的指针的大小都一样,给指针分配存储空间的时候不需要知道它指向的对象的类型细节,见示例8-5。
示例8-5

 

struct A
{
int  count ;
char *pName;         // A holds-a string
B  *pb ;           // A holds-a B
};
struct B
{
char ch ;
  *pa;         // B holds-a A
  *pNext ;     // B自引用
};

 

上面的两个结构可以组成一个链表,A是链表头的类型,B是链表节点的类型。通过链表头节点可以遍历整个链表,每个链表节点还可以指向另一个链表,……,这样就形成了一个庞大的链式结构。    
利用对象之间的引用关系,我们就可以实现链表、树、队列等复杂的数据结构,或者实现一些复杂的对象管理,比如对象之间的索引和定位。

 

【提示8-4】:
C++和C都支持相同类型对象之间的直接赋值操作(默认的“operator=语义”,就是对象按成员拷贝语义),但是不能直接比较大小和判断是否相等。

 

这是因为,相同类型对象的各数据成员在内存中的布局是一致的,编译器执行默认的位拷贝也是符合赋值操作语义的。而出于对齐(将大小调整到机器字的整数倍)的考虑,每个对象的存储空间中可能会存在填补字节,这些字节单元不会初始化而是具有上次使用留下的“脏值”(随机值)。显然每个对象填补字节的内容是不会相同的。这就是说,如果编译器支持使用逐位比较的默认方法来比较同类型对象,结果肯定是不对的,而有意义的大小关系是与具体应用相关的,显然编译器并不对应用领域的东西做任何假设。例如:
Student a, b;
cout << ((a.ID > b.ID) ? "a larger than b" : "a less than b") << endl;
所以,当默认的赋值语义不能满足我们的要求的时候,就需要定义自己的赋值语义。在C语言中只有定义一些函数来完成这样的功能,而C++则提供了运算符重载机制可以解决赋值和比较等问题。(本质上仍然是函数调用,只是形式不同而已!)
 
本文节选自《高质量程序设计指南:C++/C语言》

林锐,韩永泉编著
电子工业出版社出版
分享到:
评论

相关推荐

    77G 22套C语言 C++ 数据结构 程序设计视频课程合集 C丨C++相关学习视频全套视频教程

    C语言高级教程 - 指针和结构体 01.PonC_指针-1.mp4 02.PonC_指针-2.mp4 03.PonC_指针实例.mp4 04.PonC_指针运算.mp4 05.PonC_指针和函数.mp4 06.dk2j_cpp_函数指针.mp4 07.PonC_转换表.mp4 08.PonC_指针和...

    C++习题和答案

    类是抽象数据类型的实现; B.类是具有共同行为的若干对象的统一描述体; C.类是创建对象的样板; D.类就是C语言中的结构类型; 8. C++对C语言作了很多改进,下列描述中( )使得C语言发生了质变,即从面向过程变成为面向...

    C++小游戏源代码+资源合集

    C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计。 ...

    21天学通Visual C++(第2版)

    其中,第1篇是C++数据表达篇,包括C++入门、变量和数据类型、运算符和表达式以及程序控制结构等;第2篇是C++面向过程设计篇,包括函数、编译预处理、数组、指针和构造数据类型等内容;第3篇是C++面向对象编程篇,...

    C++高级参考手册 完全自学 内容详细 讲解通俗易懂

    2.7 抽象数据类型 2.8 对象细节 2.9 头文件形式 2.10 嵌套结构 2.11 小结 2.12 练习 第3章 隐藏实现 3.1 设置限制 3.2 C++的存取控制 3.3 友元 3.3.1 嵌套友元 3.3.2 它是纯的吗 3.4 对象布局 3.5 类 48...

    C++基础教程完整版

    4. 高级数据类型 Advanced Data 1. 数组 Arrays 2. 字符序列 Character Sequences 3. 指针 Pointers 4. 动态内存分配 Dynamic memory 5. 数据结构 Data Structures 6. 自定义数据类型 User defined data...

    C++源代码+资源合集

    C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计。 ...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    2.13.1 C++/CLI特有的基本数据类型 84 2.13.2 命令行上的C++/CLI输出 87 2.13.3 C++/CLI特有的功能—— 格式化输出 88 2.13.4 C++/CLI的键盘输入 91 2.13.5 使用safe_cast 92 2.13.6 C++/CLI枚举 92 2.14 ...

    C语言和C++语言的区别是什么?快速入门C++编程的方法解析.docx

    C++是一种高级编程语言,是C语言的一种扩展和改进。C++最早是由Bjarne Stroustrup在20世纪80年代初期开发的,主要目的是为了扩展C语言的功能,使其能够支持面向对象的编程。因此,C++包括了C语言的所有特性,同时还...

    eclipse 开发c/c++

    在 Eclipse 中安装 CDT 之后, 浏览至 File =&gt; New =&gt; Project,在那里,您将发现三个新的可用项目类型:C(“Standard C Make Project”)、C++(“Standard C++ Make Project”)和“Convert to C or C++ Projects...

    《由浅入深学C++-基础、进阶与必做300题》pdf格式 (完整版 带目录

    《由浅入深学C++:基础、进阶与必做300题(含DVD光盘1张)》涉及面广,从基本知识到高级内容和核心概念,再到综合案例,几乎涉及C++开发的所有重要知识。本书适合所有想全面学习C++开发技术的人员阅读,尤其适合没有...

    C语言与C++的主要区别是什么

    此外,C++还增加了一些新的数据类型和函数库,使得程序员能够更方便地进行编程和调试。虽然C++的代码通常比C语言的代码更加复杂和庞大,但是在一些特定的应用场景下,其性能优势可以得到体现。通过学习和掌握这两种...

    学会C语言之后还有必要学习C++吗?具体运用C++编写的代码解析.docx

    模板是C++中一种泛型编程技术,它允许程序员编写通用的代码,从而可以在不同的数据类型上重用代码。STL是C++中一个非常重要的库,它提供了许多基本数据结构和算法,如向量、列表、堆、排序等等。使用STL可以让程序员...

    C++ 大学自学教程 C++ 大学教程 第7版 2

    5,C++数据类型结构 6,结构和联合 7,C++数据进阶 8,指针、地址和动态存储器 9,引用变量 10,递归 11,预处理器 12,函数模板 13,程序组织结构 第二部分 使用类 14,C++类 15,构造函数和析构函数 16,转换 17,...

    Dev-C++编译程序器

    C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。 C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计...

    C++代码合集.rar

    C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计。 ...

    c++大学自学教程 c++大学教程 第七版1

    5,C++数据类型结构 6,结构和联合 7,C++数据进阶 8,指针、地址和动态存储器 9,引用变量 10,递归 11,预处理器 12,函数模板 13,程序组织结构 第二部分 使用类 14,C++类 15,构造函数和析构函数 16,转换 17,...

    C++基础入门知识总结.rar

    C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计。 ...

    c++大学自学教程 c++大学教程 第七版 4

    5,C++数据类型结构 6,结构和联合 7,C++数据进阶 8,指针、地址和动态存储器 9,引用变量 10,递归 11,预处理器 12,函数模板 13,程序组织结构 第二部分 使用类 14,C++类 15,构造函数和析构函数 16,转换 17,...

    Visual C++ 2005入门经典--源代码及课后练习答案

    2.9.1 C++/CLI特有的基本数据类型 87 2.9.2 命令行上的C++/CLI输出 91 2.9.3 C++/CLI特有的功能——格式化输出 92 2.9.4 C++/CLI的键盘输入 94 2.9.5 使用safe_cast 95 2.9.6 C++/CLI枚举 96 2.10 小...

Global site tag (gtag.js) - Google Analytics