导航:首页 > 源码编译 > 跨编译单元抽象

跨编译单元抽象

发布时间:2022-07-02 22:25:48

‘壹’ 抽象类的抽象方法可以用private修饰吗

C#用多种修饰符来表达类的不同性质。根据其保护级C#的类有五种不同的限制修饰符: public可以被任意存取; protected只可以被本类和其继承子类存取; internal只可以被本组合体(Assembly)内所有的类存取,组合体是C#语言中类被组合后的逻辑单位和物理单位,其编译后的文件扩展名往往是“.DLL”或“.EXE”。 protected internal唯一的一种组合限制修饰符,它只可以被本组合体内所有的类和这些类的继承子类所存取。 private只可以被本类所存取。 如果不是嵌套的类,命名空间或编译单元内的类只有public和internal两种修饰。 new修饰符只能用于嵌套的类,表示对继承父类同名类型的隐藏。 abstract用来修饰抽象类,表示该类只能作为父类被用于继承,而不能进行对象实例化。抽象类可以包含抽象的成员,但这并非必须。abstract不能和new同时用。下面是抽象类用法的伪码: abstract class A{public abstract void F();}abstract class B: A{public void G() {}}class C: B{public override void F(){//方法F的实现}}抽象类A内含一个抽象方法F(),它不能被实例化。类B继承自类A,其内包含了一个实例方法G(),但并没有实现抽象方法F(),所以仍然必须声明为抽象类。类C继承自类B,实现类抽象方法F(),于是可以进行对象实例化。 sealed用来修饰类为密封类,阻止该类被继承。同时对一个类作abstract和sealed的修饰是没有意义的,也是被禁止的。

‘贰’ java语言的编译环境和c++有什么区别,java的特色是什么

Java不是编译成本机机器代码,而是编译成自己的虚拟机代码,由虚拟机解释执行,C++是编译成本机代码执行的。
Java是一种跨平台,适合于分布式计算环境的面向对象编程语言。
具体来说,它具有如下特性:
简单性、面向对象、分布式、解释型、可靠、安全、平台无关、可移植、高性能、多线程、动态性等。
下面我们将重点介绍Java语言的面向对象、平台无关、分布式、多线程、可靠和安全等特性。
1.面向对象
面向对象其实是现实世界模型的自然延伸。现实世界中任何实体都可以看作是对象。对象之间通过消息相互作用。另外,现实世界中任何实体都可归属于某类事物,任何对象都是某一类事物的实例。如果说传统的过程式编程语言是以过程为中心以算法为驱动的话,面向对象的编程语言则是以对象为中心以消息为驱动。用公式表示,过程式编程语言为:程序=算法+数据;面向对象编程语言为:程序=对象+消息。
所有面向对象编程语言都支持三个概念:封装、多态性和继承,Java也不例外。现实世界中的对象均有属性和行为,映射到计算机程序上,属性则表示对象的数据,行为表示对象的方法(其作用是处理数据或同外界交互)。所谓封装,就是用一个自主式框架把对象的数据和方法联在一起形成一个整体。可以说,对象是支持封装的手段,是封装的基本单位。Java语言的封装性较强,因为Java无全程变量,无主函数,在Java中绝大部分成员是对象,只有简单的数字类型、字符类型和布尔类型除外。而对于这些类型,Java也提供了相应的对象类型以便与其他对象交互操作。
多态性就是多种表现形式,具体来说,可以用“一个对外接口,多个内在实现方法”表示。举一个例子,计算机中的堆栈可以存储各种格式的数据,包括整型,浮点或字符。不管存储的是何种数据,堆栈的算法实现是一样的。针对不同的数据类型,编程人员不必手工选择,只需使用统一接口名,系统可自动选择。运算符重载(operatoroverload)一直被认为是一种优秀的多态机制体现,但由于考虑到它会使程序变得难以理解,所以Java最后还是把它取消了。
继承是指一个对象直接使用另一对象的属性和方法。事实上,我们遇到的很多实体都有继承的含义。例如,若把汽车看成一个实体,它可以分成多个子实体,如:卡车、公共汽车等。这些子实体都具有汽车的特性,因此,汽车是它们的“父亲”,而这些子实体则是汽车的“孩子”。Java提供给用户一系列类(class),Java的类有层次结构,子类可以继承父类的属性和方法。与另外一些面向对象编程语言不同,Java只支持单一继承。
2�平台无关性
Java是平台无关的语言是指用Java写的应用程序不用修改就可在不同的软硬件平台上运行。平台无关有两种:源代码级和目标代码级。C和C++具有一定程度的源代码级平台无关,表明用C或C++写的应用程序不用修改只需重新编译就可以在不同平台上运行。
Java主要靠Java虚拟机(JVM)在目标码级实现平台无关性。JVM是一种抽象机器,它附着在具体操作系统之上,本身具有一套虚机器指令,并有自己的栈、寄存器组等。但JVM通常是在软件上而不是在硬件上实现。(目前,SUN系统公司已经设计实现了Java芯片,主要使用在网络计算机NC上。
另外,Java芯片的出现也会使Java更容易嵌入到家用电器中。)JVM是Java平台无关的基础,在JVM上,有一个Java解释器用来解释Java编译器编译后的程序。Java编程人员在编写完软件后,通过Java编译器将Java源程序编译为JVM的字节代码。任何一台机器只要配备了Java解释器,就可以运行这个程序,而不管这种字节码是在何种平台上生成的。另外,Java采用的是基于IEEE标准的数据类型。通过JVM保证数据类型的一致性,也确保了Java的平台无关性。
Java的平台无关性具有深远意义。首先,它使得编程人员所梦寐以求的事情(开发一次软件在任意平台上运行)变成事实,这将大大加快和促进软件产品的开发。其次Java的平台无关性正好迎合了“网络计算机”思想。如果大量常用的应用软件(如字处理软件等)都用Java重新编写,并且放在某个Internet服务器上,那么具有NC的用户将不需要占用大量空间安装软件,他们只需要一个
Java解释器,每当需要使用某种应用软件时,下载该软件的字节代码即可,运行结果也可以发回服务器。目前,已有数家公司开始使用这种新型的计算模式构筑自己的企业信息系统。
3�分布式
分布式包括数据分布和操作分布。数据分布是指数据可以分散在网络的不同主机上,操作分布是指把一个计算分散在不同主机上处理。
Java支持WWW客户机/服务器计算模式,因此,它支持这两种分布性。对于前者,Java提供了一个叫作URL的对象,利用这个对象,你可以打开并访问具有相同URL地址上的对象,访问方式与访问本地文件系统相同。对于后者,Java的applet小程序可以从服务器下载到客户端,即部分计算在客户端进行,提高系统执行效率。
Java提供了一整套网络类库,开发人员可以利用类库进行网络程序设计,方便得实现Java的分布式特性。
4�可靠性和安全性
Java最初设计目的是应用于电子类消费产品,因此要求较高的可靠性。Java虽然源于C++,但它消除了许多C++不可靠因素,可以防止许多编程错误。首先,Java是强类型的语言,要求显式的方法声明,这保证了编译器可以发现方法调用错误,保证程序更加可靠;其次,Java不支持指针,这杜绝了内存的非法访问;第三,Java的自动单元收集防止了内存丢失等动态内存分配导致的问题;第四,Java解释器运行时实施检查,可以发现数组和字符串访问的越界,最后,Java提供了异常处理机制,程序员可以把一组错误代码放在一个地方,这样可以简化错误处理任务便于恢复。
由于Java主要用于网络应用程序开发,因此对安全性有较高的要求。如果没有安全保证,用户从网络下载程序执行就非常危险。Java通过自己的安全机制防止了病毒程序的产生和下载程序对本地系统的威胁破坏。当Java字节码进入解释器时,首先必须经过字节码校验器的检查,然后,Java解释器将决定程序中类的内存布局,随后,类装载器负责把来自网络的类装载到单独的内存区域,避免应用程序之间相互干扰破坏。最后,客户端用户还可以限制从网络上装载的类只能访问某些文件系统。
上述几种机制结合起来,使得Java成为安全的编程语言。
5�多线程
线程是操作系统的一种新概念,它又被称作轻量进程,是比传统进程更小的可并发执行的单位。
C和C++采用单线程体系结构,而Java却提供了多线程支持。
Java在两方面支持多线程。一方面,Java环境本身就是多线程的。若干个系统线程运行负责必要的无用单元回收,系统维护等系统级操作;另一方面,Java语言内置多线程控制,可以大大简化多线程应用程序开发。Java提供了一个类Thread,由它负责启动运行,终止线程,并可检查线程状态。Java的线程还包括一组同步原语。这些原语负责对线程实行并发控制。利用Java的多线程编程接口,开发人员可以方便得写出支持多线程的应用程序,提高程序执行效率。必须注意地是,Java的多线程支持在一定程度上受运行时支持平台的限制。例如,如果操作系统本身不支持多线程,Java的多线程特性可能就表现不出来。

‘叁’ C++题目,有大佬过来看看么 定义学生类。 (1)类名:STUDENT;

预备知识:

c++中我们cpp文件和.h文件的区别是,cpp文件是需要编译的文件,成为一个独立的编译单元,而h文件从来是不需要编译,只是用于预处理。

通常我们在cpp文件中,完成函数的实现,然后在h中则是对于函数的声明,由于默认情况下,全局变量和全局函数存储类型都是extern类型的,所以我们不需要显示的使用extern

这样,我们其他的cpp文件,只要#include .h文件,则在cpp中实现的函数,就能在其他cpp中使用,因为我们已经用.h文件,完成了extern函数声明的操作。

c++类的定义,其实就是定义一个类型。

class A{

public:

void fun(int n);

int fun1(void);

public:

int num;

};

其实就是定义了一种类型。和我们通常所说的定义不一样。

类的定义,是不能重复定义的,在同一个编译单元中,只能定义类一次。如果重复定义,会出错。同时类声明和类定义都是内部链接。只是为当前编译单元所用。

因此,把类的定义,放在.h文件中,类的实现放在专门的cpp中。这样包含.h的其他cpp,就可以使用cpp中实现的函数。。

同时注意:类的实现cpp文件的编译,必须依赖于类的定义文件.h,所以我们在类实现文件cpp中必须#include< ........h>,用于编译,否则会出错。这是不同于普通的函数。

//student.h(这是头文件,在此文件中进行类的声明)
classStudent//类声明
{public:
voiddisplay();
private:
intnum;
charname[20];
charsex;
};
//student.cpp//在此文件中进行函数的定义
#include<iostream>
#include″student.h″
voidStudent∷display()
{cout<<″num:″<<num<<endl;
cout<<″name:″<<name<<endl;
cout<<″sex:″<<sex<<endl;
}
//main.cpp主函数模块
#include<iostream>
#include″student.h″
intmain()
{Studentstud;
stud.display();
return0;
}

类中只有static成员变量。具有外部链接特性。所以这要求,static成员变量的初始化一定要在.Cpp文件中。如果在h文件中。那么多个cpp文件#include,则发生多次重复定义的错误。

类定义和类实现分离的好处:

1/快编译速度

当然可以啊。相反,如果你把类的所有代码都内联定义到头文件中,那么所有需要用到这个类的CPP文件实际上都包含了更多的代码,编译器编译每个这样的CPP文件时都编译了这些代码。而分开定义,这些代码就只被编译了一遍,也就是在编译实现那个类的CPP文件时。

在特殊情况下确实可以的
假如我有一个类a被几百个cpp同时包含,如果定义和声明放在一起,只要我对a进行任何修改,那几百个文件都必须被重新编译。而如果头文件只包含声明,修改a的实现不会导致那些文件被重新编译,有时可以极大的提高速度

在C++中,类的定义方法如下:

class 类名{
访问范围说明符:
成员变量1
成员变量2
成员函数声明1
成员函数声明2

访问范围说明符:
更多成员变量
更多成员函数声明
...
};

类的定义要以;结束。

“访问范围说明符”一共有三种,分别是 public、private 和 protected。三者的区别后面会详细介绍,目前暂且都用 public。“访问范围说明符”可以出现任意多次。

“成员变量”的写法与普通的变量定义相同。称其为成员变量,是因为这些变量是一个类的成员。

同样地,“成员函数声明”的写法也与普通的函数声明相同。

一个类的成员函数之间可以互相调用。类的成员函数可以重载,也可以设定参数的默认值。

以前所学的函数不是任何类的成员函数,可称为“全局函数”。

成员变量就代表对象的“属性”,成员函数就代表对象的“方法”。成员变量和成员函数出现的先后次序没有规定。

成员函数的实现可以位于类的定义之外,格式如下:

返回值类型 类名:函数名()
{
函数体
}


定义类之后,就可以定义对象了。定义对象的基本方法如下:

类名 对象名;

此处,“对象名”的命名规则和普通变量相同。对象也可以看作“类变量”

类的示例程序剖析

下面来看一个用面向对象的方法进行 C++ 程序设计的例子。

例题:编写一个程序,输入矩形的宽度和高度,输出其面积和周长。

这个程序比较简单,实际上根本不需要用面向对象的方法进行设计,这里只是为了使读者更容易理解类和对象的概念。

首先要做的事情是分析问题中有哪些“对象”。比较明显,只有“矩形”这种对象。然后要进行“抽象”,即概括“矩形”这种对象的共同特点。

矩形的属性就是宽度和高度。因此需要两个变量,分别代表宽度和高度。

一个矩形可以有哪些方法(即可以对矩形进行哪些操作)呢?在本程序中,矩形可以有设置宽度和高度、计算面积和计算周长三种行为,这三种行为可以各用一个函数实现,它们都会用到宽度和高度这两个变量。

“抽象”完成后,就可以用 C++ 提供的语法特性写出一个“矩形类”,将矩形的属性和方法“封装”在一起。程序如下:

#include <iostream>
using namespace std;
class CRectangle
{
public:
int w, h; //成员变量,宽和高
void init( int w_,int h_ ); //成员函数,设置宽和高
int area(); //成员函数, 求面积
int perimeter(); //成员函数,求周长
}; //必须有分号
void CRectangle::init( int w_,int h_ )
{
w = w_; h = h_;
}
int CRectangle::area()
{
return w * h;
}
int CRectangle::perimeter()
{
return 2 * ( w + h);
}
int main( )
{
int w,h;
CRectangle r; //r是一个对象
cin >> w >> h;
r.init( w,h);
cout << "It's area is " << r.area() << endl;
cout << "It's perimeter is " << r. perimeter();
cout << sizeof(CRectangle) << endl;
return 0;
}

‘肆’ 什么是语言描述方法有哪些分类急!请以<荷塘月色>234段为例说明!

第一节、设计和开发的原则

关于程序设计的指导性原则没有唯一的标准,这里显示已被应用的一种可选方案。

一、软件开发的“流程化”

1)软件开发的各个环节是相互依存的

软件开发过程当中不只有程序设计环节中的编码和调试。

设计、编码、调试可被明确定义,在实际开发中不能严格在时间和空间上独立,彼此是相联系,互相作用的。

2)明确的开发模型

避免“瀑布式”的开发模型,在目标明确的情况下,整体性地关注并适当进入细节,不应彼此脱节。

3)理想的开发流水线

二、软件开发的“结构化”

1)严格的框架式开发

编码员和设计者分工时,遵循标准的框架,可以获得较少的通信和经验总结。

2)严格的规范

制定有效的命名法,文档格式,注释,调试等指导性原则,在适当的地方更灵活地完成,而在一些地方按照基本法则行事。

三、软件开发的“模块化”

1)可选材料的改制和扩展

内部改制可能是不被允许的,但在原规范上的、基于某种具备前瞻性的实现手段的扩充可能总是可行的,前者形如已编译链接库中的代码片断,后者可基于类和模板的实现。或者实现为一类组件对象模型的用户,向下派生出具备扩充能力的兼容接口。

2)尽可能设计满足通用需求的组件

设计通用的组件不一定是最高效的,但应该具备广泛的兼容性,以便在各种场合均能派上用场。为此局部系统在实现的时候将可能作一些妥协。

3)简单、易用、可维护性的接口和实现

一个系统应建立于多个抽象层次之上,并使每个层次有着类似的应用规范,即它们都具有:

1、界面

2、上下文数据

3、同抽象层次间的通信

4、可扩展

5、信息隐蔽

6、可被视为完整、独立的逻辑单元

四、软件开发的“简洁性”

任一种程序设计范性的最一般表述层:引导对象和行为的呈现,应基于可被良好理解的一般性的逻辑结构上,过于追求“拟真”的效果和“华丽”的语法修饰可能为项目带来效率、维护等方面不可预知的额外开销。

第二节、发现和描述类

一、类设计的主要环节

1)基本概念、相互关系

2)初始化和简要操作

3)细化、依赖关系

4)公用操作、内部构造、扩展接口等界面实现

二、发现类

1)从应用出发,兼顾已有实现

2)函数对象是一种拟函式,仿照函数语法,操控符是带有参数和函数指针的特殊类,通过流的输入、输出运算符的重载,影响流的行为

3)附带类“特征”、“分类”信息的继承结构,使得使用者可根据其特性选择不同动作,通过重载 / RTTI 及其他簿记机制等

4)用例(use case)是一个系统在一次特定使用的描述,发现更多的用例,且不能指望得到所有的用例(排除部分使用情况有限的系统功能),了解哪些是主要的,哪些是次要的。以完成当前这一轮的设计和实现工作

5)不要尝试设计“理想系统”

三、描述操作

1)寻找最小的操作集

2)提供协助函数

3)过于单一的实现策略和对资源的依赖将限制系统的可扩展性、依赖性

4)操作的类别

1、对象构成

2、询问

3、更新

4、行为、生成

5、附加的操作符、迭代器、切割符

上述操作相互之间可能是互相覆盖:迭代器被解引用将查询数据,被执行偏移将可能引起数据的读出或者写入;更新的操作可以返回旧值以备用;行为、生成操作可能引起状态更新,如文件读写,或者导致不支持再读取 / 写入状态,如关闭一个文件。

C++ 中对上述类型操作的支持技术:

1、对象构成:构造、复制构造、序列输入、析构

2、询问:const 支持对常量对象的优先访问和承诺不修改对象内部信息

3、更新:static 静态数据成员是对象间共享的,任何一个对象对该成员的更改将影响其他对象;private 数据仅由当前对象的成员方法或者友元访问,外部访问通过指定方法修改更新而不涉及过多的细节,使用虚函数形成界面规范;protected 则向派生类公开数据访问的权限

4、行为、生成:运算符重载、类型转换

5、迭代器、切割器:迭代器将当前对象的数据索引形成包装在一种类似指针行为的对象中,由其代为迭代元素;切割器接受有关规则描述,以及宿主对象的数据索引形式(如数据区指针)

四、依赖关系

不应试图让一个类作所有的事情,有一些局部性工作的类或者过程完成这些操作。

五、界面分离

为用户和扩展分别提供公用界面和保护界面。

一个类应提供位于同一个抽象层次的操作。

* 在“设计中心”实现更一般的类。

第三节、系统设计要点

一、参照模型

为新项目的实施选择可参照的已有模型,使设计者可以立即将注意力投入到一些精确的描述细节。

从模型可选方案中去考察应用到当前项目的可行性,并从其获得帮助,而不是一种纯粹的模仿,“设计的重用”。

二、模式

为设计中的一般性元素,设计针对问题、使用条件等的描述的建档,称为“模式”。

三、原型的构造

1)暗礁

原型可以忽视内部实现技巧、效率,并因此近于产品的外在特性,而可能被改良作为产品发布,并由此再在不良设计之上进行投入,并试图在下一个版本进行性能优化,但这是违背原型在设计中的作用。

2)原型是为了分析构建细节和考察可行方案,性能和设计不是其注意力

四、调试

测试时,尝试设计一些特定数据结构用于记载程序运行状态和调试数据,并可能在适当的时候请求假定用户的帮助。

assert 断言分析一段代码的状态码真值,该断言在发布版本中被自动忽略,使用 exception 异常类的抛出以便总是产生程序中断。

五、软件维护

1)什么是软件维护

重新设计和重新实现。

2)什么有利于软件维护

1、命名、格式规范

2、明确的类层次和构造法则

3、易于拆解或分析的依赖性

4、界面友好、可扩展和移植

3)不利于软件维护的项目给维护人员带来的问题

1、可能仅靠实现去推测系统构成

2、局部补丁使系统不断恶化

六、优化

1)为支持有效性能优化、局部调整,应当给出清晰简单的设计

2)不要依赖直觉

七、可重用

1)在某个确定的框架,可简化工作量的类型或者实用代码

2)有意识地为重用涉及代码,为未来的维护和可移植性提供空间

八、维持设计的完整性

每个小组成员进入细节的时候应仍把握他们的整体设计。

“整体设计文档”——一小部分人应总是关心整体性的把握。

九、个人

1)一个组织指望一个可互换的团队,意味着不允许他们利用自己更加擅长的技能高于实现所需的能力进行更好的工作

2)管理者应具备相当的技能和教育以便评估小组和项目的完成进度和小组成员的完成质量,而不是简单地依靠:

1、代码行数

2、错误修复数

3、运行时效率、外观

这会导致:

1、拒绝重用

2、故意制造错误

3、为优化局部性能而写出脆弱的难看的代码

3)关键技术人员应保持其低替换率

管理者需要他们以及最新的相关技术知识。关键技术人员也需要胜任的管理者支持以及非技术环境的最低程度的理解。

4)不希望改变的人应适从于不需过多改变的领域而不是在一个显着改变了成功条件的地方

第四节、语言相关的设计要点

一、熟悉语言特性

描述语言特性的材料的选择可能影响到设计本身,设计者需要熟知语言的特性,避免在特定语言中表现不佳的应用方式。

二、不同语言环境下的设计经验

在其他语言环境中的“不适当”的经验用于当前语言可能引入歧途,“错误、生硬、低效率”。

在其他语言环境中的“过时”的经验将不能发挥当前语言界已实现的更高抽象能力,而产生冗长乏味的重复代码片断。

使用其他语言的经验可能带来偏见,不能正确地看待当前语言环境的设计哲学。

在实现一种设计中,设计师优先从语言支持的特性中寻找直接对应的语法工具,而后才考虑更“笨拙”的实现。

明确在当前语言中能可用的,但被广泛描述为“不建议使用”的语法特性,了解这么做的原因。

三、依赖关系的语言特性支持

在语言特征上支持依赖关系,使得显式声明彼此相关 / 无关的部分成为可行的、有帮助的。并给其他用户提供良好的规范用法以及为帮助程序提供产生依赖图的参考。

四、C++ 提供的语法工具

1)类信息、隐藏、依赖关系的描述

2)虚函数为继承依赖关系维护一张调用表,以便总是访问正确的方法,如虚析构函数

3)模板提供参数表,以在不同用户需求时编译成不同的专门化版本

4)异常借助异常类的组织和堆栈回退对象析构机制来更好地管理和处理运行时异常

五、对 C++ 使用的偏差

1)C 子集,而忽略类

2)类的实施,而忽略类继承关系

3)“纯”面向对象。而忽视不能对象化的一般全局概念

所有这些不适当的使用都可能导致并不是很良好的设计并可能带来不必要的负担。

六、忽视类

1)代码无法直接反映设计,缺乏类型系统

2)可能需要有高端的设计生成器

3)功能分解的设计将抵触试图进行的类风格的应用

4)不同的实现者可能不能协调,为了进行并行开发可能需要关注彼此更多的实现

5)公共数据对任何成员也是可访问的,它可能散布在大量的实现细节中

6)类应总是被作为设计时的一种思考方式,而不是僵硬的使用

七、忽视继承

1)原因可能是“继承”破坏了信息隐藏,任何有心者可能会继承一个已有类型,并暴露出内部信息

2)合理使用保护成员

3)什么时候使用继承:集合处理(接受基类指针的集合)、扩展描述一种设计概念

八、静态类型检查

编译器的静态类型检查要求更良好的类型传递和使用,通过精确描述和强类型界面为编译时提供良好的参考。

缺失的静态类型检查意味着不完整的规范。

* 为访问到共同基类界面的集合操作使用基类指针传递,为访问到明文一致的不同基类的操作使用模板函数。

九、实现良好的框架和系统

1)不论在哪个领域十分成功的应用框架,都不能消除程序设计的工作。应用框架在涉及更为精细的实践中,其有效性会迅速降低

2)有优势的规范系统应在代码结构、外观、基本思想与直接语言环境能够良好融合

第五节、类的设计和实现

一、类的相互关系

二、类的分类

1)用户类

2)资源类:硬件访问、系统抽象

3)结构

4)内部类型、辅助结构

三、类的抽象层次

避免无意义的跨抽象层次的描述形式。

四、派生类和基类的类间依赖

一般认为派生类总是依赖于基类。同时,基类也可能依赖于派生类,如虚函数和保护成员。故基类可能在不知情的情况下访问派生类的此类实现,其派生类应是可预期的行为。尽管基类对派生类的依赖不表现在明文上定义的先后顺序。

五、虚函数在基类被声明为 private 访问权限,派生类并不能直接访问,但可以在派生类重载为公有的或者保护权限的

六、包容

在类型中具有其他类型对象称为对该类型的“包容”。

1)一个引用类型的成员对象应总是在初始化式中初始化引用

2)若要在运行时更改包含对象,优先选择指针对象

3)在初始化式中的类对象构造式

class ClassType {

Inner inn;

ClassType()

: inn()

{

constructor-fields

}

class-fields

};

4)明确包含的关系,优先选择对象类型

5)利用指针连接关系建立“对象层次结构”

七、is-has 关系

一个派生类对象同时是一个基类对象。

一个包容类对象具有一个成员类对象。

八、私有、保护派生称为“实现继承”

通过私有、保护派生的类型将无法隐式和静态转换到其基类 / 引用 / 指针,即使静态类型转换、基类复制构造是可行的。

和“实现继承”相对立的是公有继承中的“多态”,作为不同基类型的指针,对象具备不同的行为界面。

九、A 类使用 B 类的情况

1)声明为友元类,或 B 类的某个成员函数作为 A 类的友元

2)调用 / 读写 B 类的相关成员

3)显式 / 隐式创建 B 类对象,如作为函数返回值等

4)取 B 类型大小

十、为设计概念找到一种到程序设计的一一映射关系,并总是贯彻它,以支持语言不直接具有的特性

十一、类的不变式

1)具有对象的引用 / 指针成员,形成对象层次结构,这些值称之为状态

2)满足某种限定条件的状态,这种限制条件称为对象的不变式

3)利用异常类,限定条件枚举,检查函数等在执行状态更新时维护“不变式”逻辑

4)断言,对不符合某种条件的表达式执行一种预定义动作

标准库函数 cassert 在不设置 NDEBUG 预处理器符号的情况下,输出错误文件名、行号,并执行 abort()。

十二、组件

1)可用于构造组件的设施:类、类继承、名称空间

2)类界面不应当依赖于类实现所依赖的环境

十三、模板

虚基类和模板分别支持对象化的和参数化的通用算法技巧。前者基于统一基类的虚函数表提供一致界面,后者基于提供明文一致的函数形式以及。虚方法占用运行时时间,模板占用编译时时间。

十四、界面与实现

1)使用包含类型的指针,引用以避免界面上的依赖,并在实现上使用此类指针 / 引用数据的成员,称为类定义界面和实现的分离

2)界面上包含成员定义将使实现向编译单元透明,以便在线化,其中涉及的细节应避免过于依赖的外部类型的成员访问

3)界面上的成员定义尽可能使用简单类型和标准库类型

4)交集不明显,但因错误 / 不良的设计指导风格而制定兼容多种界面的基类的形式易产生“肥大的界面”

第六节、运行时的类

一、具体类型

1)描述一种实体概念,不作为类层次结构的一部分

2)具体类型具有最小的依赖性

3)具体类的用户对类实现反应敏感

4)具体类借助模板算法提供编译时类型检查,以提供灵活的 / 不损失性能的处理机制。为此为近似的操作提供明文一致的界面,或者使用辅助类型暴露通用的界面,如迭代器

二、抽象类

1)抽象类作为一系列派生类型的类型并定义一组通用的操作界面

2)依赖于抽象基类的用户代码在实际构造时使用的派生类发生修改时(派生类的定义和实例化位于和使用基类的用户代码不同的编译单元),不引起重新编译

3)抽象类为有意义的操作界面维护在虚函数表,无法完成在线化

三、结点

有意的概念描述类的实现称为“结点”,结点使用基类服务,并具有一定的构造策略和实现,但并不被推荐构造最终的对象,而是作为该对象的局部实现。

通过 RTTI 尝试访问预期的界面。

四、动作

可以使用类以描述一组动作。

1)实现的动作需要在不同的场合携带有效数据并以一种预期的格式传递,则该动作并不能由纯粹的函数实现,如算法中的仿函式和输入 / 输出流的操控符

2)仿函式使用 operator() 作为动作识别,规范操控符分别将可能格式登记到 operator << 或 operator >> 运算符的重载列表中,并利用打包对象对输入 / 输出流状态执行更改

3)设计使用不同的动作识别,作为一组动作序列统一进行处理

五、界面类

1)类型检查、异常处理在界面类中进行处理,并将实现交给其基类或者包含的类对象 / 指针 / 引用

2)多重继承的同名现象,使用包装界面类提供新的可覆盖虚函数间接访问原有同名虚函数的实现,原虚函数将通过私有继承而限制后续派生类的访问

3)为什么需要界面类:

1、向当前的设计风格妥协

2、适合于某种约定界面限制,以执行通用算法

3、符合某种文化差异

4、隐藏不安全的界面

4)界面类又称为包装类 wapper。

六、句柄

1)为利用虚函数机制,需要使用指针、引用操作,这需要用户参与管理。句柄是一种替为管理资源的类对象,并尽可能维护一组系统化的句柄类型

2)提供了比指针更多的附加信息

3)可以进行引用计数

4)应该是代价低廉的

‘伍’ Turbo C V2.0

C++笔记(一)
变量与函数

变量声明:
extern int i;
函数声明:
extern float f(float);
float f(float); //extern在此不是必须的
float f(float a); //声明中起作用的只有类型,与变量名无关
变量定义:(变量在第一次定义时被声明)
int i;
函数定义:(有函数体的函数声明就成了函数定义)
float f(float a) { return a + 1; }
有了声明就必须有定义,有了定义声明就可以不要,因为定义包含了声明。

函数在定义时可以有未命名的参数。比如某参数在函数体中未被使用,则删去此参数的名称后编译时就不会有变量未使用的警告。

func()在C++中表示没有参数的函数,而在C中表示不确定的参数(这种情况下就会忽略类型检查)。func(void)在C和C++中都表示没有参数的函数。

C++中可以在程序的任何地方定义变量,但有些格式是不允许的。比如:
for (int i = 0, int j = 0; ...; ...;) // 只允许在for中定义一个变量
while ((char c = cin.get()) != 'q') // 定义变量时不允许使用括号

局部变量都是auto的,因此没有必要显式地使用auto来修饰局部变量。

只能在块中定义register变量。register变量不能被取址。(不推荐使用)

定义为static的变量可以在块外继续存在,但却只能在块内被访问。比如在函数中定义时可以像全局变量一样维持原有的值,但不像全局变量可以被程序的其他部分修改。(静态存储)

static修饰全局变量或函数时表示该变量或函数只在当前文件中有效(内部联接)。(静态可见性)

用extern修饰的变量或函数的定义可以在其他文件中出现(外部联接)。全局变量和函数默认都是extern的(C++中的const除外)。

const修饰的变量在C++中只在文件内部有效(内部联接),若要使它在其他文件中有效需要加上extern修饰。而在C中的const默认是extern的,因此不同文件中不允许有重名的const。

使用volatile修饰的变量会在每次需要的时候被重新读取,编译器不会对其加优化。

在成员函数里要使用同名的全局变量或函数时,在全局的函数名或变量名前加::,表示全局的域。

结构和数组

被嵌套的结构不能访问嵌套其的结构私有成员,必须先声明被嵌套的结构,再声明其为friend,最后定义此结构。

对象在域结束后会被自动销毁。析构函数会被自动调用,即使是使用goto语句跳出域也一样。但是使用C标准库中的setjmp()和longjmp()跳出时析构函数不会被调用。

如果给定一个数组的初始值小于数组的大小,数组中剩余的元素会被初始化为0。如:
int b[6] = {0};
但只有数组定义没有初始值时就没有这种效果。

结构体的初始化也一样:
struct X {
int i;
float f;
char c;
};
X x1 = { 1, 2.2, 'c' };
但当结构体中有构造函数时,必须通过构造函数初始化:
struct Y {
float f;
int i;
Y(int a);
};
Y y1[] = { Y(1), Y(2), Y(3) };

union中也能定义访问控制、成员函数和构造/析构函数,但是union不能被继承。

也可以定义匿名的union,这时访问union中的成员不需要加上域标识符。

const

const int* u;和int const* v都表示指向常量的指针。int* const w;才表示指针常量。即const在*左边表示指向常量的指针,const在*右边表示指针常量。也可以理解为const只修饰它左边的东西。

字符串数组应该是常量,因此像char* cp = "howdy";这样的写法按理不能修改字符串的内容,而应该写成char cp[] = "howdy";。但为了与C代码兼容,编译器通常会容许这个错误。

只有不是const的返回值能被用作lvalue,因此若不希望函数返回的对象被用作lvalue,可以用const来修饰。

在表达式或函数求值的过程中编译器生成的临时对象都是const的。因此如果函数要以引用方式接受临时对象作为参数时,参数引用也需要用const修饰。

类中的const只表示在初始化后不能改变,需要在构造函数初始化列表中初始化。如:
Fred::Fred(int sz) : size(sz) {}
这就使得const能在构造函数被定义时就被初始化。对于非const的类型的初始化也可以这样进行。对于内置类型的初始化也可以用这种像是调用构造函数的形式。

类中以static修饰的const变量表示被这个类的所有对象共用。必须在定义时就被初始化。在旧的编译器下,一般使用enum来代替。如static const int size = 100;就用enum { size = 100 };代替。

被声明为const的对象只能调用其为const的成员函数。声明为const的成员函数中不能对对象的内容进行修改或调用非const的成员函数。定义时const放在函数参数列表之后(放在前面会与const的返回值混淆)。

非const的对象也能调用const的成员函数。因此把不会改变对象内容的成员函数定义为const的就能具有最大的通用性。构造函数和析构函数不能是const的。

在const的成员函数中修改对象的成员:
1、将this转换为普通的指针(const成员函数中的this是const指针),就能通过其来改变对象的成员,如:
((Y*)this)->i++;

(const_cast<Y*>(this))->i++;
2、以mutable修饰的成员能在const的成员函数中被修改。

关于预处理

#define的常量用const取代,宏用inline函数取代。

在类定义中定义的函数自动为内联函数。也可以使用inline修饰函数使其成为内联的。inline只有在函数定义时才会发挥作用。因此头文件中的内联函数都是有函数体的。inline的函数也是内部联接的。
friend的函数也可以是inline的。

inline只是对编译器的优化提示,编译器不一定会对所有的inline进行内联。

不可替代的预处理功能:
字符串化(#,将标识符转化为字符串):#define DEBUG(x) cout << #x " = " << x << endl
记号粘贴(##,连接两个标识符形成一个新的标识符):#define FIELD(a) char* a##_string; int a##_size

static

编译器保证对static的内置类型赋初值。对于static的对象调用其默认的构造函数。

static对象的析构函数会在main()函数结束或调用exit()时被自动调用。因此在static对象的析构函数中调用exit()可能会导致递归调用。调用abort()时不会自动调用static对象的析构函数。

全局的对象是static的。因此全局对象的构造和析构函数可以用来在进入main()之前和退出main()之后完成一些工作。

对于全局的对象来说,因为其本身是静态存储的,因此使用static修饰的效果只是使其变为内部联接。

静态数据成员的定义:
class A {
static int i;
public:
//...
};
int A::i = 1;
只有static const的整型才能在类内部被初始化,其他类型都必须在外部(文件域中)被初始化。因此本地类(在函数中定义的类)中不能定义静态成员。

static的成员函数在类中定义。为所有该类的对象共用,调用时可以直接使用类名::函数名。

static的成员函数只能访问static的成员,因为static的成员没有this。

static初始化的依赖关系(最好避免)。可参考iostream的实现(cin、cout和cerr都是在不同文件中的static对象)或另一种(居然无名!)方法。

namespace

#include <iostream.h>
意味着
#include <iostream>
using namespace std;
因为在标准化之前不存在名字空间,.h中的所有内容都是暴露的

namespace定义只能出现在全局域或另一个namespace中。定义的{}后没有分号。可以跨越多个文件定义(不算重复定义)。也可以定义别名。如:
namespace Bob = BobsSuperDuperLibrary;

每个编译单元都有一个未命名的namespace。通过将变量放入这个namespace中就可以不必将它们声明为static的(内部联接)。C++中要达到文件内的静态就使用这种方法。

类中声明的友元也会进入这个类所在的namespace。

using namespace只在当前域内有效(将namespace中的所有名称注入当前域)。using namespace中引入的名称可以被覆盖。

联接方式。比如需要在C++中调用C的库中的函数:
extern "C" float f(int a, char b);
否则的话联接程序可能无法解析函数调用。通常编译器都会自动处理这种情况。

引用和指针

引用必须在创建时被初始化为引用某一个变量,并且一旦在初始化后就不能再改变被引用的对象。

函数返回引用时要注意引用的对象不能在函数返回后就不存在了,比如函数内部的变量。

拷贝构造函数接受的是本类的引用。定义拷贝构造函数就必须定义构造函数。

通过声明一个private的拷贝构造函数可以防止对象被传值。

指向特定类中成员的指针:
声明:
int ObjectClass::*pointerToMember;
初始化:
int ObjectClass::*pointerToMember = &ObjectClass::a;
使用:
objectPointer->*pointerToMember = 47;
object.*pointerToMember = 47;
对于成员函数也适用

运算符重载

重载的运算符必须接受至少一个自定义类型。接受的参数都为内置类型的运算符无法被重载。

运算符作为类的成员函数被重载时,类的对象就作为第一个参数。注意此时函数的返回方式。

重载++a会调用operator++(a),重载a++会调用operator++(a, int),其中第二个int参数是不会被用到的,只是用来区分前缀和后缀调用。--的重载也是一样。

=和[]只能作为成员函数被重载。()只能作为成员函数被重载,可以带任意多个参数。(若可以不作为成员函数被重载,则对于内置类型的运算就可以被重载,这是没有意义的)

->和->*也只能作为成员函数被重载,但对返回值有一定的限制。

.和.*不能被重载

返回值优化:
Integer tmp(left.i + right.i);
return tmp;
这样编译器需要三步才能完成(构造,拷贝,析构),而
return Integer(left.i + right.i);
则只需要一步

重载时作为成员或非成员函数的选择:
所有的一元运算符 推荐作为成员
= () [] -> ->* 必须作为成员
+= -= /= *= ^=
&= |= %= >>= <<= 推荐作为成员
所有其他的二元运算符 推荐作为非成员

当对象还没有被创建时,=调用的是构造函数或拷贝构造函数,为的是初始化对象;当对象已被创建时,=调用的才是operator=。因此
Fee fee(1);
Fee fum(fi);
这样的写法要比
Fee fee = 1;
Fee fum = fi;
这样的写法清晰。

在重载赋值运算符时,首先检查是否是对自身赋值是个好习惯。

当对象中有指针时,拷贝构造函数通常需要连同复制出指针所指向的内容。而当此内容很大时,通常采用引用计数的方法,只在需要修改数据且引用数大于1时才复制内容。这种技术被称为-on-write。

当构造函数接受一个其他类型的对象作为参数时,编译器可以用它来进行自动类型转换。如果不需要这样的自动转换,在构造函数前加上explicit。

也可以重载operator 类型名 来定义自动类型转换。由于这种类型转换是由源对象完成的(不像构造函数的类型转换是由目标对象完成的),因此可以完成自定义对象到内置类型的转换。

运算符重载为非成员函数时,运算符两边都可以进行自动类型转换。

提供自动类型转换时注意两种类型之间只需提供一条转换路径,否则会出现二义性错误。

new和delete

new为对象分配空间并调用构造函数。delete调用对象的析构函数后释放对象的空间。

delete对于零指针无效,因此人们通常在delete之后将指针设为零,以防止多次delete带来的问题。

delete void*可能会带来问题。由于编译器不知道具体的类型,将导致对象的空间被释放而没有调用析构函数。这可能会引起内存泄漏。

a* p = new a[100];为数组中的每个对象分配空间并调用构造函数进行初始化。
delete []p;则完成相反的事。delete后的[]表示指针只是数组的首地址,编译器会自
动获取数组的大小完成释放工作。由于表示首地址,最好定义为常量:
a* const p = new a[100];

当new找不到空间分配时,会调用new-handler。其默认行为是抛出异常。可以通过使用new.h中的set_new_handler()来定义作为new-handler的函数。

operator new和operator delete都可以被重载,用来完成一些自定义的内存管理功能:
void* operator new(size_t sz)
void operator delete(void* m)

当new和delete作为类的成员函数被重载时,为该类的对象分配空间时就会调用这些重载的操作符。

用于为数组分配空间的operator new[]和operator delete[]也能被重载。实际占用的空间比分配的要多4个字节,被系统用于存储数组的大小。

重载的new可以接受任意多个参数,比如定义第二个参数,用于指明在何处分配空间:
void* operator new(size_t, void* loc)
使用时:
X* xp = new(a) X;
其中的a就是第二个参数。采用这种形式的new可能需要显式调用析构函数:
xp->X::~X();
(因为对象可能不是构建在堆上,使用delete只能释放堆上的空间)

继承

合成和继承都需要在构造初始化列表中对子对象进行初始化。

一旦在子类中定义了一个与超类中同名的函数,超类中所有同名的函数,不管函数特征是否相同,都会变得不可见。

构造函数,析构函数,operator=不会在继承时进入子类。

继承默认是private的,即超类中的public成员在子类中也会变成private的。若只想暴露某些成员,可以在子类中的public部分使用using。这种情况下同名的重载函数会被全部暴露。

多态

C++中使用virtual关键字来声明函数为延迟绑定的(或动态绑定)。函数的定义中不需要virtual。使用了virtual之后, upcast时调用的就是子类的重载函数,否则只能调用基类的。(C++使用virtual来使得动态绑定成为可选的,这是出于效率的考虑。其他语言如 Java和Python等默认就是使用动态绑定的)

拥有纯虚函数的类是抽象类(提供了一个接口)。纯虚函数在virtual定义后加上=0。

继承抽象类的子类必须实现基类中所有的纯虚函数,否则就还是抽象类。

纯虚函数不能内联定义。在定义后则可以被子类使用。

传值的upcast会把对象切割,即子类会被切到只剩下基类的部分。抽象的基类可以避免这种情况,因为抽象类不允许实例化。

构造函数不能是virtual的,而析构函数可以。

模板

继承可以重用对象,而模板可以重用代码。

定义template时可以使用内置类型。它们的值可以是常量。如:
template<class T, int size = 100>

template不仅可以用来创建类模板,还能用来创建函数模板。

杂项

之间没有标点符号的字符数组会被自动连接起来

C++标准中包含的不是STL,而是C++标准库,它是由STL演变来的。

?运算符中:的两边必须都有表达式。

以,分隔的表达式,最后一个表达式的值会被返回。

C++中的类型转换可以函数的形式,如float(n)等价于(float)n。

C++中的显示类型转换(提供一种容易辨别的形式):
static_cast:类型收窄时可以避免编译器的警告信息;同样可用于类型放宽、void*的强制转换和自动隐式转换。
const_cast:用于转换const和volatile,比如将一个普通指针指向一个const
reinterpret_cast:把一种类型当成另一种类型
dynamic_cast:用于downcast

sizeof是运算符而不是函数,用于类型时要加括号,如sizeof(double),用于变量时不用,如sizeof x。

显式运算符:
逻辑运算符
and &&
or ||
not !
not_eq !=
位运算符
bitand &
bitor |
xor ^
and_eq &=
or_eq |=
xor_eq ^=
compl ~

‘陆’ java 抽象怎么理解

  1. 抽象是一种面向对象的编程实践的核心原则, Java具有所有OOPs原则,抽象是一个Java语言的主要构建块。

  2. 数据抽象的跨度从创建简单的数据对象集合的实现复杂如HashMap或HashSet。

  3. 控制抽象是结构化编程背后主要驱动力。

‘柒’ java学习、:技术文章 |JAVA学习基础

2、源文件声明规则

当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。

一个源文件中只能有一个public类

一个源文件可以有多个非public类

源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。

如果一个类定义在某个包中,那么package语句应该在源文件的首行。

如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。

import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

类有若干种访问级别,并且类也分不同的类型:抽象类和final类等。这些将在访问控制章节介绍。除了上面提到的几种类型,Java还有一些特殊的类,如:内部类、匿名类。

3、Java包:

包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。(即包中包含很多种类的类和接口)

当编译一个.java文件(即一个编译单元)时,在.java文件中的每个类都会有一个输出文件,而该输出文件的名称与.java文件中每个类的名称相同,只是多了一个后缀名.class。因此在编译少量.java文件之后,会得到大量的.class文件。每一个.java文件编译以后都会有一个public类,以及任意数量的非public类。因此每个.java文件都是一个构件,如果希望许许多多的这样的构件从属于同一个群组,就可以在每一个.java文件中使用关键字package。而这个群组就是一个类库。

如果使用package语句,它必须是.java文件中除注释以外的第一句程序代码。

4、权限修饰符

Java中有四种权限修饰符,这些修饰符控制对类,类的成员方法,类的成员变量的访问;其作用范围如下:

public:所有的类都可以访问。

private:只有在同一个类中才可以访问。

protected:同一个类中、同一个包中、子类中都可以访问,其他包不可以。

包类型的,即默认类型同一个类中、同一个包中可以访问到,其他包和子类不可以。

5、数据类型

byte: Java数据类型和c/c++基本类似,但Java中有byte类型,表示8位2进制的整数,相当于c/c++中的char.

常量:Java中常量用final表示,相当于const,final中的值是可以改变的;

-包装类: 在开发过程中,有时候需要使用对象,而不是内置数据类型,因此java为每个内置数据类型提供了对应的包装类所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类Number的子类,对于字符也有相应的Character类。每个类具有相应的函数。

数组:java数组是对象,与c++不同,语法上与c++类似,java数组具有一个属性length可以获取数组的长度。(数组a,a.length为数组长度),Array类具有一些函数可以对数组进运算。

Java高级特性

1、Java中的接口

Java中类继承只允许一个父类,当需要进行多个继承时,需要使用接口。接口类似于类,定义时用interface替代类定义中的class,但里面的方法只有定义没有实现。接口文件的文件名必须与接口名相同。继承接口的关键字为implement,与类的继承extends相似,可以进行多继承。接口继承必须实现接口中的所有方法。

接口是规范,规定某个实现类里必须有那些方法,只要事先定义好接口,当大家一起合作的时候就不用特别麻烦的交互,而是每个人写好自己的接口实现类,最后拼接到一起就可以了比如数据库连接类,接口规定好有哪些方法、方法的功能,然后每个数据库厂商根据接口去实现具体的类,达到统一的目的,要不每个数据库厂商自己实现一套,那么你编程的时候就要根据不同驱动使用不同的方法,造成不必要的麻烦。

2、Java中的抽象类

只声明方法的存在而不具体实现的类,抽象类不能实例化(不能创建相应对象)定义方式:在class前加上abstract,ex:

abstractclass 类名{ ;;;}

抽象类中定义的没有实际意义的,必须在子类重写的方法为抽象方法,定义时前面加abstract:abstract type name(agrs)。

一般方法定义与在正常类中定义一样。

java对象创建一般方式:ClassName obj = new ClassName();

阅读全文

与跨编译单元抽象相关的资料

热点内容
喷油螺杆制冷压缩机 浏览:581
python员工信息登记表 浏览:377
高中美术pdf 浏览:161
java实现排列 浏览:513
javavector的用法 浏览:982
osi实现加密的三层 浏览:233
大众宝来原厂中控如何安装app 浏览:916
linux内核根文件系统 浏览:243
3d的命令面板不见了 浏览:526
武汉理工大学服务器ip地址 浏览:149
亚马逊云服务器登录 浏览:525
安卓手机如何进行文件处理 浏览:71
mysql执行系统命令 浏览:930
php支持curlhttps 浏览:143
新预算法责任 浏览:444
服务器如何处理5万人同时在线 浏览:251
哈夫曼编码数据压缩 浏览:428
锁定服务器是什么意思 浏览:385
场景检测算法 浏览:617
解压手机软件触屏 浏览:352