文档库 最新最全的文档下载
当前位置:文档库 › C++05

C++05

C++05
C++05

5继承与派生类

5-1 继承与派生类

5-1-1 为什么要使用继承

继承性是一个非常自然的概念,现实世界中的许多事物是具有继承性的。人们一般用层次分类的方法来描述它们的关系。例如,图5-1是一个简单的汽车分类图。

图5-1 简单的汽车分类图

在这个分类树中建立了一个层次结构,最高层是最普遍、最一般的,每一层都比它的前一层更具体,低层含有高层的特性,同时也与高层有细微的不同,它们之间是基类和派生类的关系。例如确定某一辆车是客车以后,没有必要指出它是进行运输的,因为客车本身就是从运输汽车类派生出来的,它继承了这一特性,同样也不必指出它会自行驱动,因为凡是汽车都会自行驱动。客车是从运输汽车类中派生而来,而运输汽车类又是从汽车类派生而来,因此客车也可以继承汽车类的一般特性。

所谓继承就是从先辈处得到属性和行为特征。类的继承就是新的类从已有类那里得到已有的特性。从另一个角度来看这个问题,从已有类产生新类的过程就是类的派生。类的继承和派生机制使程序员无需修改已有类,只需在已有类的基础上,通过增加少量代码或修改少量代码的方法得到新的类,从而较好地解决了代码重用的问题。由已有类产生新类时,新类便包含了已有类的特征,同时也可以加入自己的新特性。已有类称为基类或父类,产生的新类称为派生类或子类。派生类同样也可以作为基类派生出新的类,这样就形成了类的层次结构。

下面我们通过例子进一步说明为什么要使用继承。现有一个person类,它包含有name (姓名)、age(年龄)、sex(性别)等数据成员与成员函数print(),如下所示:class person {

protected:

char name[10];

int age;

char sex;

public:

void print();

};

假如现在要声明一个employee类,它包含有name(姓名)、age(年龄)、sex(性别)、department(部门)及salary(工资)等数据成员与成员函数print(),如下所示:

140

第5章继承与派生类

class employee{

protected:

char name[10];

int age;

char sex;

char department[20];

float salary;

public:

print();

};

从以上两个类的声明中看出,这两个类中的数据成员和成员函数有许多相同的地方。只要在person类的基础上再增加成员department和salary,再对print()成员函数稍加修改就可以声明出employee类。像现在这样声明两个类,代码重复太严重。为了提高代码的可重用性,就必须引入继承,将employee类说明成person类的派生类,那些相同的成员在employee类中就不需要再说明了。

5-1-2 派生类的声明

为了理解一个类如何继承另一个类,我们看一下employee类是如何继承person类的。

//定义一个基类

class person{

protected:

char name[10];

int age;

char sex;

public:

//……

};

//定义一个派生类

class employee:public person {

protected:

char department[20];

float salary;

public:

//……

};

仔细分析以上两个类,不难发现:在类名employee的冒号之后,跟着关键字public与类名person,这就意味着类employee将继承类person的全部特性。其中类person是基类,类employee是派生类。关键字public指出派生的方式,告诉编译程序派生类employee从基类person公有派生。

一个派生类只有一个直接基类的情况,称为单继承。一个派生类同时有多个基类的情况称为多继承。在此,我们先介绍单继承,多继承将在后面的章节介绍。

声明一个派生类的一般格式为:

141

class 派生类名:继承方式基类名 {

//派生类新增的数据成员和成员函数

};

这里,“基类名”是一个已经定义的类的名称,“派生类名”是继承原有类的特性而生成的新类的名称。“继承方式”规定了如何访问从基类继承的成员,它可以是关键字private、protected或public。分别表示私有继承、保护继承和公有继承。因此,由类person继承出类employee可以采用下面的3种格式之一:

(1)公有继承

class employee:public person{

//…

};

(2)私有继承

class employee:private person{

//…

};

(3)保护继承

class employee:protected person{

//…

};

如果不显式地给出继承方式关键字,系统默认为私有继承(private)。类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。

派生类除了可以从基类继承成员外,还可以增加自己的数据成员和成员函数。这些新增的成员正是派生类不同于基类的关键所在,是派生类对基类的发展。

从已有类派生出新类时,可以在派生类内完成以下几种功能:

(1)可以增加新的数据成员。

(2)可以增加新的成员函数。

(3)可以重新定义基类中已有的成员函数。

(4)可以改变现有成员的属性。

这些内容将在下面章节中详细介绍。

5-1-3 基类成员在派生类中的访问属性

基类的成员可以有public(公有)、protected(保护)和private(私有)3种访问属性,基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的对象,就只能访问该类的公有成员。

派生类可以继承基类中除了构造函数与析构函数之外的全部成员,但是这些成员的访问属性在派生过程中是可以调整的。从基类继承来的成员在派生类中的访问属性是由继承方式控制的。

类的继承方式有public(公有继承)、protected(保护继承)和private(私有继承)3种,不同的继承方式导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。

142

143

第5章 继承与派生类

在派生类中,从基类继承来的成员可以按访问属性划分为4种:不可直接访问的成员、public (公有成员)、protected (保护成员)和private (私有成员)。

表5-1 基类成员在派生类中的访问属性

从表5-1中不难归纳出以下几点: (1)基类中的私有成员

无论哪种继承方式,基类中的私有成员不允许派生类继承,即在派生类中是不可直接访问的。 (2)基类中的公有成员

当类的继承方式为公有继承时,基类中的所有公有成员在派生类中仍以公有成员的身份出现的。 当类的继承方式为私有继承时,基类中的所有公有成员在派生类中都是以私有成员的身份出现的。

当类的继承方式为保护继承时,基类中的所有公有成员在派生类中都是以保

护成员的身份出现的。

(3)基类中的保护成员

当类的继承方式为公有继承时,基类中的所有保护成员在派生类中仍以保护成员的身份出现的。

当类的继承方式为私有继承时,基类中的所有保护成员在派生类中都是以私有成员的身份出现的。

当类的继承方式为保护继承时,基类中的所有保护成员在派生类中仍以保护成员的身份出现的。

5-1-4 派生类对基类成员的访问规则

派生类对基类成员的访问形式主要有以下两种:

(1)内部访问:由派生类中新增成员对基类继承来的成员的访问。

(2)对象访问:在派生类外部,通过派生类的对象对从基类继承来的成员的访问。 下面具体讨论在3种继承方式下,派生类对基类成员的访问规则。

1.私有继承的访问规则

当类的继承方式为私有继承时,基类的public成员和protected成员被继承后作为派生类的private成员,派生类的其他成员可以直接访问它们,但是在类外部通过派生类的对象无法访问。基类的private成员在私有派生类中是不可直接访问的,所以无论是派生类成员还是通过派生类的对象,都无法直接访问从基类继承来的private成员,但是可以通过基类提供的public成员函数间接访问。下面是一个私有继承的例子。

【例5.1】一个私有继承的例子

#include

class base { //声明一个基类

public:

void setx(int n)

{ x=n; }

void showx()

{ cout<

private:

int x;

};

class derived:private base{ // 声明一个私有派生类

public:

void setxy(int n,int m)

{

setx(n); //setx()在派生类中为private成员,派生类成员函数可以访问

y=m;

}

void showxy()

{

cout<

cout<

}

private:

int y;

};

main()

{

derived obj;

obj.setx(10); // 非法,setx()在派生类中为private成员,派生类对象不能访问

obj.showx(); // 非法,showx()在派生类中为private成员,派生类对象不能访问 obj.setxy(20,30);

obj.showxy();

return 0;

}

本例中首先定义了一个类base,它有一个private数据成员x和两个public成员函数setx()和showx()。将类base作为基类,派生出一个类derived。派生类derived除继承了基类的成员外,还有只属于自己的成员:private数据成员y,public函数成员setxy()和showxy()。继承方式关键字是private,所以这是一个私有继承。

由于是私有继承,所以基类base的public成员setx()和showx()被派生类derived私有继承后,成为derived的private成员,只能被derived的成员函数访问,不能被派生类的对象访问。所以在main()函数中,对obj.setx()和obj.showx()的调用是非法的,因为这两个函数在派

144

第5章继承与派生类

生类derived中已成为private成员。

需要注意的是:无论setx()和showx()如何被一些派生类继承,它们仍然是base的public 成员,因此以下的调用是合法的:

base base_obj;

base_obj.setx(2);

虽然派生类derived私有继承了基类base,但它的成员函数并不能直接访问base的private 数据x,只能访问两个public成员函数。所以在类derived的成员函数setxy()中访问base的public成员setx()是合法的,但在成员函数showxy()中直接访问base的private成员x是非法的。

如果将例中函数showxy()改成如下形式:

void showxy(){ showx();cout<

重新编译,程序将顺利通过。可见基类中的private成员既不能被派生类的对象访问,也不能被派生类的成员函数访问,只能被基类自己的成员函数访问。因此,我们在设计基类时,总要为它的private数据成员提供public成员函数,如本例的成员函数showx()等,以便使派生类可以间接访问这些数据成员。

下面程序说明保护成员以私有方式被继承后的访问属性。

【例5.2】保护成员以私有方式被继承后的访问属性

#include

class base{ //声明一个基类

public:

void seta(int sa)

{ a=sa; }

void showa()

{ cout<<"a="<

protected:

int a;

};

class derive1: private base{ // 声明一个私有派生类

public:

void setab(int sa,int sb)

{

a=sa;

b=sb;

}

void showab()

{

cout<<"a="<

cout<<"b="<

}

protected:

int b;

};

class derive2: private derive1{ // 声明一个私有派生类

public:

void setabc(int sa,int sb,int sc)

{

setab(sa,sb);

c=sc;

145

}

void showabc()

{

cout<<"a="<

cout<<"b="<

cout<<"c="<

}

private:

int c;

};

void main()

{

base op1;

op1.seta(1);

op1.showa();

derive1 op2;

op2.setab(2,3);

op2.showab();

derive2 op3;

op3.setabc(4,5,6);

op3.showabc();

}

编译上面的程序,在行尾标有“非法”的语句上产生了错误。原因是基类base中的protected 成员a被其派生类derive1私有继承后成为private成员,所以不能被derive1的派生类derive2中的成员函数showabc()访问。derive1类中的protected成员b,被其派生类derive2私有继承后是private 成员,所以可以被derive2中的成员函数showabc()访问。

如果将例中成员函数showabc()改成如下形式:

void showabc()

{

showa();

cout<<"b="<

cout<<"c="<

}

重新编译,仍将出现错误信息。但是将函数showabc()改成以下形式后:

void showabc()

{

showab();

cout<<"c="<

}

重新编译,程序将顺利通过。请读者想一想,为什么?

经修改后,程序的运行结果为:

a=1

a=2

b=3

a=4

b=5

c=6

经过了私有继承之后,所有基类的成员都成为了派生类的private成员或不可直接访问的成员,如果进一步派生的话,基类的全部成员都无法在新的派生类中被访问。因此,私有继承之后,基类的成员再无法在以后的派生类中再发挥作用,实际是相当于中止了基类功能的

146

第5章继承与派生类

继续派生,出于这种原因,私有继承的实际应用很少。

下面,我们通过表5-2总结一下私有继承的访问规则。

表5-2 私有继承的访问规则

2.公有继承的访问规则

当类的继承方式为公有继承时,基类的public成员和protected成员被继承到派生类中仍作为派生类的public成员和protected成员,派生类的其他成员可以直接访问它们。但是,类的外部使用者只能通过派生类的对象访问继承来的public成员。基类的private成员在私有派生类中是不可直接访问的,所以无论是派生类成员还是通过派生类的对象,都无法直接访问从基类继承来的private成员,但是可以通过基类提供的public成员函数间接访问它们。

下面我们举一个公有继承的例子。

【例5.3】公有继承的访问规则

#include

class base { //声明一个基类

public:

void setxy(int m,int n)

{

x=m;

y=n;

}

void showxy()

{

cout<<"x="<

cout<<"y="<

}

private:

int x;

protected:

int y;

};

class derived: public base //声明一个公有派生类

{

public:

void setxyz(int m,int n,int l)

{

setxy(m,n); //setxy()在派生类中是public成员,可以访问

z=l;

}

void showxyz()

{

cout<<"x="<

cout<<"y="<

cout<<"z="<

}

147

int z;

};

main()

{

derived obj;

obj.setxyz(30,40,50);

obj.showxy(); //合法,showxy()在类derived中为public成员

obj.showxyz();

return 0;

}

例中类derived由类base公有派生出来,所以类base中的两个public成员函数setxy()和showxy()在公有派生类中仍是public成员。因此,它们可以分别被派生类的成员函数setxyz()和派生类的对象obj访问。基类base中的数据成员x是private成员,它在派生类中是不能直接访问的,所以在函数showxyz()中对x的访问是非法的。基类base中的数据成员y是protected 成员,它在公有派生类中仍是protected成员,所以在函数showxyz()中对y的访问是合法的。

如果将例中成员函数showxyz()改成如下形式:

void showxyz()

{

showxy();

cout<<"z="<

}

重新编译,程序将顺利通过。

修改后,程序的运行结果为:

x=30

y=40

x=30

y=40

z=50

说明:

需要再次强调,派生类以公有继承的方式继承了基类,并不意味着派生类可以访问基类的private成员。如在例5.3的派生类的成员函数。

void showxyz()

{

cout<<"x="<

cout<<"y="<

cout<<"z="<

}

中,企图访问基类base的私有成员x,是非法的,因为基类无论怎样被继承,它的私有成员对派生类而言都是不能直接访问的。

下面,我们通过表5-3总结一下公有继承的访问规则。

表5-3 公有继承的访问规则

148

第5章继承与派生类

3.保护继承的访问规则

当类的继承方式为保护继承时,基类的public成员和protected成员被继承到派生类中都作为派生类的protected成员,派生类的其他成员可以直接访问它们,但是类的外部使用者不能通过派生类的对象来访问它们。基类的private成员在私有派生类中是不可直接访问的,所以无论是派生类成员还是通过派生类的对象,都无法直接访问基类的private成员。

【例5.4】保护继承的访问规则

#include

class Base{

public:

int z;

void setx(int i)

{ x=i; }

int getx()

{ return x; }

private:

int x;

protected:

int y;

};

class Derived: protected Base{

public:

int p;

void setall(int a,int b,int c,int d,int e,int f);

void show();

private:

int m;

protected:

int n;

};

void Derived∷setall(int a,int b,int c,int d,int e,int f)

{

x=a; // 非法,在类Derived中,x为不可直接访问成员,可修改为setx(a);

y=b; // 合法,y在类derived中为protected成员

z=c; // 合法,z在类derived中为protected成员

m=d;

n=e;

p=f;

}

void Derived∷show()

{

//cout<<"x="<

cout<<"x="<

~Base(){ cout<<"Destructing baes class\n"; } //基类的析构函数

};

class Derive:public Base {

public:

Derive() //派生类的构造函数

{ cout<<"Constructing derived class\n"; }

~Derive() //派生类的析构函数

{ cout<<"Destructing derived class\n"; }

};

150

第5章继承与派生类main()

{

Derive op;

return 0;

}

程序运行结果如下:

Constructing base class

Constructing derived class

Destructing derived class

Destructing base class

从程序运行的结果可以看出:构造函数的调用严格地按照先调用基类的构造函数,后调用派生类的构造函数的顺序执行。析构函数的调用顺序与构造函数的调用顺序正好相反,先调用派生类的析构函数,后调用基类的析构函数。

5-2-2 派生类构造函数和析构函数的构造规则

当基类的构造函数没有参数,或没有显式定义构造函数时,派生类可以不向基类传递参数,甚至可以不定义构造函数。例5.5的程序就是由于基类的构造函数没有参数,所以派生类没有向基类传递参数。

派生类不能继承基类中的构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。

在C++中,派生类构造函数的一般格式为:

派生类名(参数总表):基类名(参数表)

{

// 派生类新增成员的初始化语句

}

其中基类构造函数的参数,通常来源于派生类构造函数的参数总表,也可以用常数值。

下面的程序说明如何传递一个参数给派生类的构造函数和传递一个参数给基类的构造函数。

【例5.6】当基类含有带参数的构造函数时,派生类构造函数的构造方法

#include

public:

class Base {

Base(int n) //基类的构造函数

{

cout<<"Constructing base class\n";

i=n;

}

~Base() //基类的析构函数

{ cout<<"Destructing base class\n"; }

void showi()

{ cout<

private:

int i;

};

class Derive :public Base{

151

public:

Derive(int n,int m):Base(m) // 定义派生类构造函数时,

{ // 缀上基类的构造函数

cout<<"Constructing derived class"<

j=n;

}

~Derive() //派生类的析构函数

{ cout<<"Destructing derived class"<

void showj()

{ cout<

private:

int j;

};

main()

{

Derive obj(50,60);

obj.showi();

obj.showj();

return 0;

}

程序运行结果为:

Constructing base class

Constructing derived class

60

50

Destructing derived class

Destructing base class

当派生类中含有内嵌对象成员时,其构造函数的一般形式为:

派生类名(参数总表):基类名(参数表1),内嵌对象名1(内嵌对象参数表1),…,内嵌对象名n(内嵌对象参数表n)

{

// 派生类新增成员的初始化语句

}

在定义派生类对象时,构造函数的执行顺序如下:

(1)调用基类的构造函数。

(2)调用内嵌对象成员的构造函数(有多个对象成员时,调用顺序由它们在类中声明的顺序确定)。

(3)派生类的构造函数体中的内容。

撤消对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。

下面这个程序说明派生类中内嵌对象成员时派生类构造函数和析构函数的执行顺序。

【例5.7】内嵌对象成员时派生类构造函数和析构函数的执行顺序

#include

class Base {

int x;

public:

Base(int i) //基类的构造函数

{

x=i;

cout<<"Constructing base class\n";

152

第5章继承与派生类

}

~Base() //基类的析构函数

{

cout<<"Destructing base class\n"; }

void show()

{ cout<<" x=" <

};

class Derived:public Base

{

Base d; //d为基类对象,作为派生类的对象成员

public:

Derived(int i):Base(i),d(i) //派生类的构造函数,缀上基类构造函数和

{ //对象成员构造函数

cout<<"Constructing derived class\n";

}

~Derived() //派生类的析构函数

{ cout<<"Destructing derived class\n"; }

};

main()

{

Derived obj(123);

obj.show();

return 0;

}

程序执行结果如下:

Constructing base class

Constructing base class

Constructing derived class

x=123

Destructing derived class

Destructing base class

Destructing base class

上面程序中有两个类,基类Base和派生类Derived。基类中含有一个需要传递参数的构造函数,用它初始化私有成员x,并显示出一句信息。派生类中含有基类Base的一个对象。

从程序执行的结果分析,构造函数和析构函数的执行顺序与规定的顺序是完全一致的。

说明:

(1)当基类构造函数不带参数时,派生类不一定需要定义构造函数,然而当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数。甚至所定义

的派生类构造函数的函数体可能为空,仅仅起参数的传递作用。

例如,在下面的程序段中,派生类derived就不使用参数n,n只是被传递给了基类base()。

class base{

int i;

public:

base(int n)

{

cout<<"Constructing base class\n";

i=n;

}

void showi()

{ cout<

153

class derived:public base{

int j;

public:

derived(int n):base(n)

{

cout<<"constructing derived class\n";

j=0;

}

void showj()

{ cout <

};

(2)若基类使用缺省构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:基类构造函数名(参数表)”。此时若派生类也不需要构造函数,则可不

定义构造函数。

(3)如果派生类的基类也是一个派生类,每个派生类只需负责其直接基类的构造,依次上溯。

(4)由于析构函数是不带参数的,在派生类中是否要定义析构函数与它所属的基类无关,基类的析构函数不会因为派生类没有析构函数而得不到执行,它们是各自独

立的。

下面再通过一个例子,说明派生类构造函数和析构函数的构造规则。

【例5.8】说明派生类构造函数和析构函数的构造规则

#include

class First{

private:

int a,b;

public:

First(){a=0;b=0;}

First(int x,int y)

{ a=x;b=y;}

~First(){ }

void print()

{

cout<<"\n a="<

cout<<"\n stu_no: "<

cout<<"\n score: "<

}

class UStudent:public Student {

public:

UStudent(char* name1,char* stu_no1,float score1,char* major1);

~UStudent();

void show(); //在派生类中,重新定义了成员函数show()

private:

char* major;

};

UStudent∷UStudent(char* name1,char* stu_no1 ,float score1,char* major1):Student(name1,stu_no1,score1)

{

major=new char[strlen(major1)+1];

strcpy(major,major1);

}

UStudent∷~UStudent()

{ delete []major; }

void UStudent∷show()

{

Student∷show();

cout<<"\n major: "<

}

main()

{

UStudent stu1("Liming","990201",90,"computer");

stu1.show();

return 0;

}

157

在本例的派生类UStudent中,重新定义了成员函数show(),利用派生类对基类成员的访问能力,把基类的原有成员函数show()的功能照搬过来。在面向对象程序设计中,若要对基类继承过来的某些函数功能进行扩充和改造,都可以通过这样的覆盖来实现。这种覆盖的方法,是对基类成员改造的关键手段,是程序设计中经常使用的方法。

本程序的运行结果如下:

name:Liming

stu_no:990201

score:90

major:computer

5-3-2 访问声明

我们已经知道,对于公有继承,基类的公有成员函数也就是派生类的公有成员函数,这意味着外界可以用派生类的对象调用基类的公有成员函数。但是对于私有继承,基类的公有成员函数变成了派生类的私有成员函数了。这时,外界就无法利用派生类的对象直接调用基类的成员函数,而只能通过调用派生类的成员函数(内含调用基类成员函数的语句)间接地调用基类的成员函数。请看下面的例子。

【例5.10】访问声明引例

#include

class A{

public:

A(int x1)

{ x=x1; }

void print()

{ cout<<"x="<

private:

int x;

};

class B:private A{

public:

B(int x1,int y1):A(x1)

{ y=y1; }

void print2() //通过派生类B的print2()调用基类A的print()

{ print(); }

private:

int y;

};

main()

{

B b(10,20);

b.print2();

return 0;

}

本程序的运行结果为:

x=10

如果将派生类中的语句:

void print2(){ print();}

改写为语句:

158

指针数组及指向一维数组的指针讲解

一、指针数组及指向一维数组的指针(数组指针)讲解 1、数组指针(也称行指针) 定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。 如要将二维数组赋给一指针,应这样赋值:int a[3][4];int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。 p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][] 所以数组指针也称指向一维数组的指针,亦称行指针。 2、指针数组 定义 int *p[n]; []优先级高,先与p结合成为一个数组,再由int *说明这是一个整型指针数组,它有n个指针类型的数组元素。这样赋值是错误的:p=a;只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。 如要将二维数组赋给一指针数组: int *p[3]; int a[3][4]; for(i=0;i<3;i++) p[i]=a[i]; 这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p [1]、p[2]所以要分别赋值。 这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。 还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。 比如要表示数组中i行j列一个元素: *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j] 优先级:()>[]>* 例1、下列给定程序中,函数fun()的功能是:从N个字符串中找出最长的那个串,并将其地址作为函数值返回。 #include #include #define N 4

流媒体技术简介

流媒体技术简介 流媒体技术(Streaming Media Technology)是为解决以Internet为代表的中低带宽网络上多媒体信息(以视音频信息为重点)传输问题而产生、发展起来的一种网络新技术。采用流媒体技术,能够有效地突破低比特率接入Internet方式下的带宽瓶颈,克服文件下载传输方式的不足,实现多媒体信息在Internet上的流式传输。Microsoft、Intel、apple、RealNetworks等公司在流媒体技术的发展、应用等方面都具有很强的实力。 一、流媒体技术原理 1.流媒体 "流媒体"的概念包括以下两个层面。其一,流媒体是计算机网络(尤其是中低带Internet/Intranet)上需要实时传输的多媒体文件,比如声音、视频文件。在传输前需要压缩处理成多个压缩包,并附加上与其传输有关的信息(比如,控制用户端播放器正确播放的必要的辅助信息),形成实时数据流。数据流最大的特点是允许播放器及时反应而不用等待整个文件的下载。其二,流媒体是对多媒体信息进行"流化"处理,是一种解决问题的方式,可以使视频等对实时性要求严格的多媒体文件在Internet/Intranet上在既无下载等待需求又不占用客户端硬盘空间的情况下保证实时播放。 目前Internet上比较流行的流媒体有RealNetworks的Realmedia、Microsoft的WindowsMedia以及Apple公司的Quicktime,它们包括不同的媒体内容,具有不同的流格式(StreamingFormat),都有专用的播放器。以目前网上最常见的RealMedia为例,其中包括RealVideo、RealAudio、RealFlash(RealNetworks公司与Macromedia公司新近合作推出的一种高压缩比动画格式),专用播放器是RealPlayer。传输过程中通过MIME (MultiPurposeInternetMailExtensions,多用途邮件扩展)识别流媒体类型。 2.流媒体技术体系的关键技术--压缩编码技术 压缩编码技术是流媒体技术体系中的关键技术。压缩编码的基本原理是采用一定的编码方式,将文件的数据结构进行重组,一方面,去掉一些重复或占而不用的空间,以达到减小文件尺寸的目的;另一方面,将文件分成压缩包,形成数据流,将原有的多媒体文件转化为具有流格式的流媒体。 例如,Microsoft采用MPEG4(最新版本为版本3)视频压缩编码算法,能够基于视频内容编码,生成ASF格式流媒体,同时支持多带宽、高带宽视频压缩编码,可以针对不同的网络环境生成包含几种不同传输速率数据流的视频流,为高级流技术的运用提供了可能性。 3.流式传输 以视频文件为例,压缩处理后的视频文件被分成一些小片段(CliP),当用户端发出请求后,由服务器向用户端连续、实时传送这些小片段,用户端利用解压设备(播放器)对压缩过的视频片段解压后进行播放和观看。在用户端播放小片段之前,这些小片段已经存入用户机的内存,而在播放前一片段的同时,后续片段继续在后台从服务端以

数组名和指针的区别(小结)

在Win 32 中: 1. (1) 对数组名进行sizeof运算时,结果是整个数组占用空间的大小; (2) 但是数组作为函数参数时,对数组名进sizeof 运算,结果为4; (2) 对指针进行sizeof运算得到的值是编译器分配给指针(也就是一个地址)的内存空间,即为4。 2. (1) 对数组名作&运算,得到的还是数组第一个元素的地址; (2) 对指针取地址时得到的结果是指针所在的地址,也就是指向这个指针的指针,与指针的值不同。 BOOL mytest(char param[100]) { //参数数组名:paramBytes int paramBytes = sizeof(param); printf("paramBytes \t%d\n",paramBytes); //数组:mych1[] char mych1[] = "abcdefg123"; //若定义为mych1[100] int mych1Bytes = sizeof(mych1); printf("mych1Bytes \t%d\n",mych1Bytes);//输出:100 //数组:mych[200] char mych[200] = {0}; int myBytes = sizeof(mych); printf("myBytes \t%d\n",myBytes); printf("---addr \t%d\n",mych); printf("---addr \t%d\n",&mych); //指针:pch char* pch = "12345abc"; int pchBytes = sizeof(pch); printf("pchBytes \t%d\n",pchBytes); //pch = mych; printf("---addr \t%d\n",pch); printf("---addr \t%d\n",&pch); return TRUE; } 运行:

C 的封装性、继承性和多态性概念

C++的封装性、继承性和多态性概念 封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。例如,在抽象的基础上,我们可以将时钟的数据和功能封装起来,构成一个时钟类。按c++的语法,时钟类的声明如下:class Clock { public: //共有成员,外部借口void SetTime(int NewH,int NewM,int NewS); void ShowTime(); private: //私有成员,外部无法访问int Hour,Minute,Second; } 可以看到通过封装使一部分成员充当类与外部的接口,而将其他的成员隐蔽起来,这样就达到了对成员访问权限的合理控制,使不同类之间的相互影响减少到最低限度,进而增强数据的安全性和简化程序的编写工作。什么是多态(Polymorphisn)?按字面的意思就是“多种形状”。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等 的技术,赋值之后,>>>父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作<<<(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数(Virtual Function)实现的。好,接着是“虚函数”(或者是“虚方法”)。虚函数就是允许被其子类重新定

义的成员函数。而子类重新定义父类虚函数的做法,称为“覆盖”(override),或者称为“重写”。“继承”是面向对象软件技术当中的一个概念。如果一个类A继承自另一个类B,就把这个A称为"B的子类",而把B称为"A的父类"。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。 ... 继承是指一个对象直接使用另一对象的属性和方法。事实上,我们遇到的很多实体都有继承的含义。例如,若把汽车看成一个实体,它可以分成多个子实体,如:卡车、公共汽车等。这些子实体都具有汽车的特性,因此,汽车是它们的"父亲",而这些子实体则是汽车的"孩子"。19. 多态的作用?主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用; 2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

互联网中的流媒体技术

互联网中的流媒体技术 概述 随着经济的进展和科学技术的进步,人类社会已进入了信息化的新时代。internet网的飞速进展,使人们对信息时代的网络经济有了全新的认识;每一次的创新,就有一次的飞跃;每一种业务的应用,确实是一次想象力的考查。 internet网的种种应用,都阻碍着人们的工作和生活,推动社会经济的进展,从而形成一个和能源、材料一样成为当今社会的三大支柱产业之一。而流媒体技术(streaming media)作为internet网的应用之一,自产生以来,就注定要被广泛应用。 什么是流媒体 互联网的普及和多媒体技术在互联网上的应用,迫切要求能解决实时传送视频、音频、运算机动画等媒体文件的技术,在这种背景下,因此产生了流式传输技术及流媒体。通俗的讲,在互联网上的视音频服务器将声音、图像或动画等媒体文件从服务器向客户端实时连续传输时,用户不必等待全部媒体文件下载完毕,而只需延迟几秒或十几秒,就能够在用户的运算机上播放,而文件的其余部分则由用户运算机在后台连续接收,直至播放完毕或用户中止操作。这种技术使用户在播放视音频或动画等媒体的等待时刻成百倍的减少,而且不需要太多的缓存。 流媒体指在internet/intranet中使用流式传输技术的连续时基媒体,如:音频、视频或多媒体文件,它在播放前并不下载整个文件,只将开始部分内容存入内存,其他的数据流随时传送随时播放,只是在开始时有一些延迟,其关键技术确实是流式传输。 与传统的单纯的下载相比较,流媒体具有明显的优点: 由于不需要将全部数据下载,因此等待时刻能够大大缩短; 由于流文件往往小于原始文件的数据量,同时用户也不需要将全部流文件下载到硬盘,从而节约了大量的磁盘空间; 由于采纳了rstp等实时传输协议,更加适合动画、视音频在网上的实时传输。

浅析流媒体技术的发展现状

浅析流媒体技术的发展现状 随着互联网大发展的时代到来,我国的互联网技术飞速发展和普及,以网络作为传播平台的第四代媒体中独特的一种媒体“流媒体”凭借其体积小、信息量大等特点已经日益流行。本文就流媒体技术的应用和研究现状进行了阐述、对流媒体的发展前景进行了展望。 标签:流媒体流技术网络传输 0引言 我国互联网技术的发展和个人计算机的普及,网络从最初的传播文字、图片等资料到现在的各种形式的网络视频和三维动画,人们获取信息的形式呈现多样化。然而,我们不得不正视一个问题,上网人数的增加和网络设备的局限性直接让文件的大小成为网络传输过程中一个必须重视的问题,一方面:在网络上看到生动、清晰的多媒体信息演示;另一方面:网络速度制约着文件的下载时间。在这种情况下,流媒体技术应运而生。流媒体是指在互联网上以数据流的方式实时发布音频,视频.动画或者其他多媒体文件的媒体,实质是应用流技术在网络上传输的多媒体文件,数据从发送源端同时向目的接收端传输,它可以作为连续实时流在目的地被接收。其原理是将连续的多媒体文件或信息进行压缩处理后放到网络服务器上,让浏览者一边下载一边观看收听,而不需要整个多媒体文件下载完成就可以即时观看的技术,它不是一门单一的技术,融合了多种网络技术和其他计算机技术,包括流媒体数据的采集、压缩、编码、存储、网络传输和网络通信等多种技术。 1流媒体技术的应用现状 现在,流媒体已经逐渐发展成为一个朝阳产业。有专家预言,流媒体将成为未来因特网上应用的主流,实现沟通和传播的多向性,使传播不再受到时间和空间的限制。流媒体技术广泛用于新闻出版、证券、娱乐、电子商务、远程培训、视频会议、远程教育、远程医疗等互联网信息服务的方方面面,总结起来有三大应用。 1.1网络视频直播目前,流媒体技术作为第四代媒体技术中的一种,很多大型的新闻娱乐媒体,如中央电视台和一些地方电视台等,都在互联网上提供基于流媒体技术的节目,目前流媒体的视频直播应用突破了网络带宽的限制,实现了在低带宽的环境下的高质量影音传输,其中的智能流技术保证不同连接速率下的用户,使得用户可以随时随地应用流媒体技术在网络上观看多媒体信息。 1.2远程教育Internet的使用开创了远程教育的里程碑,它促进了远程教育中的教学传递日趋现代化,这种教育形式能跨越校界、区界甚至国界。流媒体技术应用突破传统的远程教育以文本为主、没有声音和视频,解决了教学模式单一、交互性差的问题。教学模式多样化体现在教师的在线直播授课和授课视频观看,

第5章函数

第5章函数 练习题5 5.1判断题 √1.函数的调用可以嵌套,函数的定义不能嵌套。 ×2.C++语言中,函数可以用原型说明,也可用简单说明。 ×3.定义函数时,存储类可以缺省,数据类型也可以省略。 ×4.函数可以没有参数,但是不能没有返回值。 ×5.函数定义时必须给出函数体,函数体内至少有一条语句。 √6.没有参数的两个函数是不能重载的。 √7.函数调用方式有传值调用和引用调用两种,传值调用中又分传值和传址两种。√8.函数的存储类有外部的和静态的两种,它们的作用域分别是程序级的和文件级的。×9.没有返回值的函数不能设置为内联函数。 ×10.函数可以设置默认的参数值,默认参数值必须设置在函数定义时的形参上。5.2单选题 1.当一个函数没有返回值时,该函数类型应说明为( A )。 A.void B.int C.无D.任意 2.下列关于设置函数默认的参数值的描述中,错误的是( C )。 A.可对函数的部分参数或全部参数设置默认值 B.在有函数说明时,默认值应设置在函数说明时,而不是定义时 C.设置函数默认参数值时,只可用常量不可用含有变量的表达式 D.设置函数参数默认值应从右向左设置 3.下列关于被调用函数中return语句的描述中,错误的是( D )。 A.一个函数中可以有多条return语句 B.return语句具有返回程序控制权的作用 C.函数通过return语句返回值时仅有一个 D.一个函数中有且仅有一条return语句 4.函数返回值的类型是由( B )决定的。 A.调用该函数的调用函数的类型 B.定义该函数时所指定的类型 C.return语句中表达式的类型 D.接收函数返回值的变量或对象的类型 5.下列设置函数参数默认值的说明语句中,错误的是( C )。 A.int fun(int x , int y=10); B.int fun(int x=5, int =10); C.int fun(int x=5, int y); D.int fun(int x , int y=a+b); (其中,a和b是已定义过具有有效值的变量) 6.下列选择重载函数的不同实现的判断条件中,错误的是( D )。 A.参数类型不同B.参数个数不同 C.参数顺序不同D.函数返回值不同 7.已知:int fun (int &a),m=10;下列调用fum()函数的语句中,正确的是( C )。

实验三 数组与指针实验

实验三数组与指针实验 【实验目的】 1.学习使用数组数据对象。 2.掌握指针的使用方法。 3.学习通过动态内存分配实现动态数组的定义和使用,并体会指针在其中的作用。4.练习通过Debug观察指针的内容及其所指对象的内容。 【实验内容】 1.运行下列程序,观察运行结果。 (1)#include class ArrayElem { int x; public: ArrayElem(int i){x=i;} //带参数的构造函数 int getx(){return x;} }; int main() { ArrayElem obs[4] ={-1,-2,-3,-4}; //创建对象数组并初始化 for(int i=0;i<4;i++) cout<<"obs["< class NumClass { int num; public: void set_num(int val){num=val;} void show_num(){cout<<"The num is :"<set_num(20);p->show_num(); //通过指针调用成员函数 return(0); } (3)#include class NumClass

指针与数组练习题

指针与数组练习题 1、下面程序实现如下功能:输入一个整数字符串转换为一个整数值,如”1234”转换为1234,”-1234”转换为-1234。读懂main函数,编写转换函数chnum #include #include void main() { char s[6]; int n; int chnum(char *p); gets(s); if (*s=='-') n=-chnum(s+1); else n=chnum(s); printf("%d\n",n); } int chnum(char*p) { int sum=0; while(*p!='\0') { if(*p>='0'&&*p<='9') sum=sum*10+*p-'0'; p++; } return sum; } 2、从键盘输入一个字符串,去掉所有非十六进制字符后转换成十进制数输出。读懂以下main函数,编写相应的函数del16和htod。 #include #include void main() { char s1[10],s2[10]; void del16(char*,char*); int htod(char*); gets(s1); //读入一字符串 del16(s1,s2); //去掉所有非十六进制字符到s2 printf("%d\n",htod(s2)); //把s2转换为10进制 }

void del16(char*s1,char*s2){ for(;*s1!='\0';s1++) if(*s1>='0'&&*s1<='9'||*s1>='a'&&*s1<='f'||*s1>='A'&&*s1<='F'){ *s2 = *s1; s2++;} *s2='\0'; } int htod(char*s2){ int sum=0; for(;*s2!='\0';s2++){ if(*s2>='0'&&*s2<='9') sum=sum*16+*s2-'0'; else if(*s2>='a'&&*s2<='f') sum=sum*16+*s2-'a'+10; else if(*s2>='A'&&*s2<='F') sum=sum*16+*s2-'A'+10; } return sum; } 3、编写函数insert(char *s1,char *s2,int pos),实现在字符串s1中的指定位置pos处插入字符串s2。 Happy Year New 7 Happy New Year #include #include int main(void) { void insert(char *s1,char *s2,int pos); char s1[80],s2[80]; int pos; gets(s1); gets(s2); scanf("%d",&pos); insert(s1,s2,pos); puts(s1); return 0; } void insert(char *s1,char *s2,int pos) {

流媒体技术的原理、应用及发展

摘要:Internet的迅猛发展和普及为流媒体业务发展提供了强大的市场动力,流媒体业务正日益普及,流媒体技术广泛应用于互联网信息服务的方方面面。首先介绍了流媒体技术的基础、基本原理以及流式传输的基本过程,接着重点介绍了流媒体技术在视频点播、远程教育、视频会议和Internet直播方面的应用,最后介绍了流媒体技术的发展现状和展望。 关键词:多媒体通信,多媒体业务,流媒体,流式传输,原理,应用,发展 随着现代网络技术的发展,网络开始带给人们形式多样的信息。从在网络上出现第一张图片到现在各种形式的网络视频、三维动画,人们的视听觉在网络上得到了很大的满足。但人们又面临着另外一种不可避免的尴尬:在网络上看到生动清晰的媒体演示的同时,不得不为等待传输文件而花费大量时间。为了解决这个矛盾,一种新的媒体技术应运而生,这就是流媒体技术。 流媒体是指在网络中使用流式传输技术的连续时基媒体,如音频、视频或多媒体文件。而流式传输技术就是把连续的声音和图像信息经过压缩处理后放到网站服务器上,让用户一边下载一边收听观看,而不需要等待整个文件下载到自己的机器后才可以观看的网络传输技术。 目前,在网络上传输音视频(A/V)等多媒体信息主要有下载和流式传输两种方案。一方面,由于音视频文件一般都较大,所以需要的存储容量也较大;同时由于受网络带宽的限制,下载这样的文件常常需要几分钟甚至几小时,所以采用下载方法的时延也就很大。而采用流式传输时,声音、图像或动画等时基媒体由音视频服务器向用户计算机连续、实时传送,用户只需经过几秒或数十秒的启动时延而不必等到整个文件全部下载完毕即可观看。当声音、图像等时基媒体在客户机上播放时,文件的剩余部分将在后台从服务器上继续下载。流式传输不仅使启动时延大大缩短,而且不需要太大的缓存容量。流式传输避免了用户必须等待整个文件全部下载完毕之后才能观看的缺点。一、流媒体技术基础 实现流式传输有两种方法:实时流式传输(Real-time streaming transport)和顺序流式传输(progressive streaming transport)。一般来说,如为实时广播,或使用流式传输媒体服务器,或应用实时流协议(RTSP)等,即为实时流式传输。如使用超文本传输协议(HTTP)服务器,文件即通过顺序流发送。采用哪种传输方法可以根据需要进行选择。当然,流式文件也支持在播放前完全下载到硬盘。 1.实时流式传输 实时流式传输总是实时传送,特别适合现场广播,也支持随机访问,用户可快进或后退以观看后面或前面的内容。但实时流式传输必须保证媒体信号带宽与网络连接匹配,以便传输的内容可被实时观看。这意味着在以调制解调器速度连接网络时图像质量较差。而且,如果因为网络拥塞或出现问题而导致出错和丢失的信息都被忽略掉,那么图像质量将很差。实时流式传输需要专用的流媒体服务器与传输协议。 2.顺序流式传输 顺序流式传输是顺序下载,在下载文件的同时用户可观看在线内容,在给定时刻,用户只能观看已下载的部分,而不能跳到还未下载的部分。由于标准的HTTP服务器可发送顺序流式传输的文件,也不需要其他特殊协议,所以顺序流式传输经常被称作HTTP流式传输。顺序流式传输比较适合高质量的短片段,如片头、片尾和广告,由于这种传输方式观看的部分是无损下载的,所以能够保证播放的最终质量。但这也意味着用户在观看前必须经历时延。顺序流式传输不适合长片段和有随机访问要求的情况,如讲座、演说与演示;也不支持现场广播,严格说来,它是一种点播技术。

变量的指针和指针变量的区别是什么

2变量的指针和指针变量的区别是什么。 答;一个变量的地址指出了变量的存储单元在内存中的具体位置,能对变量进行存取操作。这个变量的地址就是变量的指针。指针是一种具有特殊意义的整型数,指针不能存放在一般的整型变量中,必须存放在专门指针的变量中,这类变量就是指针变量。 3 一维数组元素的引用有哪些方式。 答;下标法、地址法、指针法 4 2维数组列地址有哪些计算方法。 答;1 根据数组元素所在的行计算出行地址,然后把行地址转换成行中首元素的地址,再根据数组元素所在的列计算数组元素的地址。 2 根据2维数组的数组元素在存储空间上按行连续存放的特点,每个数组元素的地址等于2维数组元素的首元素地址加上该数组元素相对于首元素位置的偏移量。 3把2维数组的每一行当作一个一维数组,用一维数组元素地址的计算方法计算相应的2维数组元素的地址。 第9章结构体与共用体 1 什么是链表。其中单向链表具有哪些特点。 答;链表是若干个同样类型的结构通过依次串接方式构成的一种动态数据结构。链表中的每一个结构体数据成为结点,链表可以分成单向链表和双向链表 单向链表的特点;1 链表中的结点数据可以改变的 2 结点占用的内存是动态分配内存和动态释放内存函数。 2 对单向链表的常用操作有哪些。 答;对单向链表的常用操作有建立、显示、插入,删除和查找。 3 什么是共用体。 答;共用体是一个集合体。它的各个成员的数据类型可以是不相同的,所有成员共享同一段存储空间,存储空间的大小取决存储单元最大的成员的数据类型。 4 指向结构体类型变量的指针变量引用形式有哪些。 答;有两种形式;【星号指针变量名】。成员名和指针变量名-大于号成员名。 第10章位运算及编译预处理 1 C提供的编译预处理功能有哪些。如何实现。 答;功能有三种;宏定义、文件包含和条件编译,分别用宏定义命令、文件包含命令、条件编译命令实现。 2 文件包含的基本功能是什么。 答;文件包含处理是一个源文件可以将另一个源文件的全部内容包含到本文件中来,作为本文件的一部分,这可以节省程序设计人员的重复劳动。 【3【在C语言中提供了几种什么样的位运算符。 答;-、小于小于、大于大于、 4 文件包含需要注意哪些问题 答;一个井include命令只能指定一个被包含文件,包含多个文件侧需多个井include命令;文件包含可以嵌套,即一个被包含文件中可以包含另一个被包含的文件;在井include命令中,文件名可以用双引号或尖括号括起来。 第11章文件 1 文件的结束标志有哪些。 答;每个文件都有一个结束标志。当文件的位置指针移到文件的结束标志处时,表示文件结束。如何测试文件是否结束,常有2种方法 1 ASCII码文件的结束标志用【-1】表示。

c多态性与虚函数习题

1.概念填空题 1.1 C++支持两种多态性,分别是静态和动态。 1.2在编译时就确定的函数调用称为静态联编,它通过使用重载函数实现。 1.3在运行时才确定的函数调用称为动态联编,它通过虚函数来实现。1.4虚函数的声明方法是在函数原型前加上关键字 virtual 。在基类中含有虚函数,在派生类中的函数没有显式写出virtual关键字,系统依据以下规则判断派生类的这个函数是否是虚函数:该函数是否和基类的虚函数参数个数相同 /同名;是否与基类的虚函数相应类型相同;是否与基类的虚函数返回值类型相同。如果满足上述3个条件,派生类的函数就是虚函数。并且该函数覆盖基类的虚函数。 1.5当通过指针或应用使用虚函数时,C++会在与对象关联的派生类中正确的选择重定义的函数。实现了运行时多态。而通过对象使用虚函数时,不能实现运行时多态。 1.6 纯虚函数是一种特别的虚函数,它没有函数的函数体部分,也没有为函数的功能提供实现的代码,它的实现版本必须由派生类给出,因此纯虚函数不能是。拥有纯虚函数的类就是抽象类,这种类不能建立对象。如果纯虚函数没有被重载,则派生类将继承此纯虚函数,即该派生类也是纯虚函数。 1.7 类的构造函数不可以(可以/不可以)是虚函数,类的析构函数可以(可以/不可以)是虚函数。当类中存在动态内存分配时经常将类的析构函数声明成虚函数。 2.简答题 2.1在C++中,能否声明虚构造函数?为什么?能否声明虚析构函数?为什么? 不可以声明纯构造函数,可以声明纯析构函数,因为C++中构造函数不能够被继承,而唇函数具有强继承性 2.2 什么是抽象类?抽象类有何作用?可以声明抽象类的对象吗?为什么? 一个类中至少有一个纯纯虚函数 抽象类中的纯虚函数可以在抽象类中定义,也可以是从它的抽象类中继承下来重订于定义 可以声明抽象类的指针和引用 2.3多态性和虚函数有何作用? 2.4是否使用了虚函数就能实现运行时的多态性?怎样才能实现运行时的多态性? 2.5为什么析构函数总是要求说明为虚函数? 3.选择题 3.1在C++中,要实现动态联编,必须使用( D )调用虚函数。 A.类名 B.派生类指针 C.对象名 D.基类指针 3.2下列函数中,不能说明为虚函数的是( )。 A.私有成员函数 B.公有成员函数 C.构造函数 D.析构函数 3.3在派生类中,重载一个虚函数时,要求函数名、参数的个数、参数的类型、参数的顺序和函数的返回值( )。 A.相同 B.不同 C.相容 D.部分相同 3.4当一个类的某个函数被说明为virtual时,该函数在该类的所有派生类中()。 A.都是虚函数

C语言中指针、数组和引用例子实例

一、指针:内容是指示一个内存地址的变量;类型是指示编译器怎么解释指针内容指向地址中的内容,以及该内存区域有多大; 例子: [cpp] int i = 0; int * pi = &i; printf(“pi = %x \n”, pi); // 打印pi的内容: 0x2000 printf(“*pi= %d \n” , *pi); // 打印pi指向地址中的值: 5 printf(“&pi= %x \n”, &pi); // 打印pi的地址: 0x100 从汇编的角度来看,指针是这样的: int i = 0; 010E139E mov dword ptr [i],0 int * pi = &i; 010E13A5 lea eax,[i] 010E13A8 mov dword ptr [pi],eax 二、数组:是一个单一数据类型对象的集合。其中单个对象没有被命名,通过索引访问。 数组名和指针的区别:数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组。数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量。指向数组的指针则是另外一种变量类型,仅仅意味着数组的存放地址 注意:虽然数组名可以转换为指向其指代实体的指针,但是它只能被看作一个指针常量,不能被修改,如下:天骄无双:https://www.wendangku.net/doc/bf4893223.html, [cpp] int intArray[10]; intArray++; // 错误 “指针和数组等价”说的是什么?索引操作相同,例如:p[2]; a[2]; 三、引用(reference)是一个对象的别名。用对象初始化引用后,对象的名字和引用都指向该对象; 引用是如何实现的?从汇编语言的角度来看,指针和引用是一样的: [cpp] int i = 0; 00E9139E mov dword ptr [i],0 int & ref = i; 00E913A5 lea eax,[i] 00E913A8 mov dword ptr [ref],eax int * pi = &i; 00E913AB lea eax,[i] 00E913AE mov dword ptr [pi],eax 指针和引用的区别(从C++使用角度来看): 不存在空引用 引用要初始化 引用初始化后,不能指向另一个对象 这是由编译阶段保证的。 备注:一个指向非常量的引用不能用字面值或者临时值初始化;但是一个指向常量的引用可以。天骄无双:https://www.wendangku.net/doc/bf4893223.html,

浅谈流媒体发展现状

浅谈流媒体发展现状与趋势 【摘要】流媒体是一项很重要而先进的通信技术,其重要原因与目前社会的信息饱和有关,信息如此之多,人们必须努力找出对自己有用的信息。无论何时何地,人们都需要导航到最简捷、最易吸收的信息,人们需要不用浪费太多时间就能获得重要的信息,流媒体技术正好符合要求,因为它内容丰富、可搜索、听众范围广,而且视频比其他形式的信息更容易吸收。【关键字】发展现状发展趋势流媒体技术 一.发展现状 当流媒体在实时应用中(如现场流媒体广播),根据当前的网络状况和用户的终端参数,多媒体数据是一边被编码一边被流媒体服务器传输给用户。而在其他的非实时应用中,多媒体数据可以被事先编码生成多媒体文件,存储在磁盘阵列中。当提供多媒体服务时,流媒体服务器直接读取这些文件传输给用户,这样服务方式对设备的要求较低。目前许多流媒体服务属于后一种方式,这样就要求流媒体服务器具有一定的机制来适应网络状况和用户设备。 流媒体技术广泛用于新闻出版、证券、娱乐、电子商务、远程培训、视频会议、远程教育、远程医疗等互联网信息服务的方方面面,总结起来有三大应用。 网络视频直播目前,流媒体技术作为第四代媒体技术中的一种,很多大型的新闻娱乐媒体,如中央电视台和一些地方电视台等,都在互联网上提供基于流媒体技术的节目,目前流媒体的视频直播应用突破了网络带宽的限制,实现了在低带宽的环境下的高质量影音传输,其中的智能流技术保证不同连接速率下的用户,使得用户可以随时随地应用流媒体技术在网络上观看多媒体信息。 远程教育Internet的使用开创了远程教育的里程碑,它促进了远程教育中的教学传递日趋现代化,这种教育形式能跨越校界、区界甚至国界。流媒体技术应用突破传统的远程教育以文本为主、没有声音和视频,解决了教学模式单一、交互性差的问题。教学模式多样化体现在教师的在线直播授课和授课视频观看,学员可以由针对性的选择想要学习的章节和内容,极大的提高了学习的效率节省时间。此外,流媒体技术也使远程教育的交互从单向通信的方式,如通过Email、在线聊天、BBS等。采用流媒体技术,把流式视频、音频加入答疑系统将提高它的完整性和交互能力。流媒体的VOD技术还可以进行交互式教学,达到因材施教的目的。像Flash、Shockwave等技术就经常应用到网络教学中。学生可以通过网络共享学习经验。大型企业可以利用基于流媒体技术的远程教育对员工进行培训。 视频点播及电视电话会议视频会议系统指互联网上或者其它数据网络上开展的一种交互式多媒体通信业务。视频会议系统与流媒体技术应用相结合,利用流媒体技术的良好的可访问性、可扩展性和对带宽的有效利用性,实现视频会议内容的广播和录播,并且由于流媒体终端播放软件大多是免费的,因此利用流媒体机制:点对点(unicast)、多址广播(Multicast)和广播(Broadcast)可以很好地满足视频会议的如上需求:首先可以使大量的授权流媒体用户参加到视频会议中,扩大了会议的规模和覆盖面;而且利用流媒体技术的记录功能,视频会议在召开完以后可以实时存储,流媒体用户就可以通过点播的方式来访问会议的内容。 二.发展趋势 流媒体的出现实现了从简单的文字和图片传输到音频和视频传播的过渡,这不仅是传播科技的一次革新,也是传播力量的再一次突破:a.流媒体传播继承了传统广电传播多维、生动、具象的特点,使得以文字图片为主体的网络新闻一改往日单维、静止、抽象的形象,推动了诸多媒体相互叠加并且高度融合的多维传播时代的到来,大大增加了传统新闻的深度和

第五章函数试题带答案

第五章试题 一.填空题 1、从字符变量S 中的第5个字符起取6个字符的VB 表达式是 Mid(s,5,6)。 2、数学表达式 e x 对应的VB 表达式为 3、数学公式))()((c s b s a s s ---的VB 表达式为______________ 4、)lg(30sin 3||32xy y x +-- 的VB 表达式为:_________________。 5、有如下声明: Dim x As Integer, y As Single 那么,x +y 的运算结果的数据类型为__single_________。 二、选择题 1、表达式100 & "100" + 100的值是(D ) A 、300 B 、100100100 C 、200100 D 、100200 2、表达式16/4-2^5*8/4 Mod 5\2的值为 B 。 A 、14 B 、4 C 、20 D 、以上值均错 3、下面四个表达式中其值为0的是( C ) A. 4/5 B. 5 mod 4 C. 4\5 D. 4 mod 5 4、已知a=”0123456789”,则表达式Val(Mid(a,5,2) +Left(a,5))的值为 A 。 A 、4501234 B 、1279 C 、451234 D 、49 5、在VB 中,能正确表达“X 是小于100的非负数”的表达式是 A A 、X>=0 And X<100 B 、0==0 Or X<100) D 、X>=0 Or X<100 6、设有如下语句( )。 Dim a, b As Integer c = "西北农林科技大学" d = #1/20/2007# 以下关于这段代码的叙述中错误的是( A )。 A 、a 被定义为Integer 类型变量 B 、b 被定义为Integer 类型变量 C 、c 中的数据是字符串 D 、d 中的数据是日期类型 7、设x=10,y=20,以下不能在窗体上显示出“A=30”的语句是( A )。 A 、Print A=x+y B 、Print "A="; x + y C 、Print "A=" & x + y D 、Print "A=" + Str(x + y) 8、表达式Len("VB 程序设计")的值为( A )。 A 、6 B 、12 C 、10 D 、5 9、下面表达式的值为真的是(D ) A.“ABC ”>”Aba ” B.“3+2”>”4” C.“ABC ”>”ABC ” D.“ABC ”>”ABB ” 10、表达式 5 Mod 3+3\5*2的值是 B A 、0 B 、2 C 、4 D 、6 11、设x=4,y=8,z=7,以下表达式的值是 C x >z Or z

指针与数组 函数的组合

指针和数组 ? ? 1.指针数组:是其数组元素为指针的数组。记住:是一个存放着指针的数组,而不是一个指针 ?定义格式为:数据类型* 数组名[数组长度] ?如:int * a[10] ; [ ]的优先级高于*,意味着使得a是一个指针数组,表示具有10个元素的数组,每个元素是一个指向int 型的指针。 ? ?2,指向数组的指针:是一个指针,指向的是一个数组。 ?定义格式为:数据类型(*数组名) [数组长度] ?如:int (*a) [10];*先于a 结合,意味着a 是一个指针,指向具有10个int 值的数组, ? ? ?指针与函数 ? ?1, 函数的指针:首先它是一个指针,指向函数的入口地址;在C语言中,函数名就是来标识函数的入口地址。 ?与指向数组的指针不同,在数组中,可以对数组中的元素访问,可以进行算术运算。 而在函数中,只需考虑函数的入口地址,而不考虑函数中某具体指令或数据所在存 储单元的地址,即不能进行算术运算 ?定义格式:存储类型数据类型(*函数名) ( ) ?如:static int (*p) (); ?存储类型表示函数在文件中的存储方式 ?数据类型表示被指函数的返回值的类型 ?最后的空括号表示指针变量所指的是一个函数 ? ?如何用指向函数的指针变量的方式来调用相应的函数: ?1), 使用前,必须先定义并赋值 ?2), 指向函数的指针定义形式中的数据类型必须和赋给它的函数返回值类型相同 ?3), 指向函数的指针只能指向函数的入口,而不能使用*(p+1) 来表示函数的下一命令?4), 在函数指针赋值时,只需给出函数名而不需给参数,如p = max; ?5), 调用时,只需将(*p) 代替函数名即可,在p 后面的括号中根据需要写上实参,如: c = (*p) (a,b) ; ?如下程序:求直角三角形的斜边 ?#include ? #include ?main() ?{ ? int a ,b ,c , f() , (*f1)(); ? a = 3; b = 4;

C语言指针数组函数练习(含参考答案)

作业(使用指针、数组、函数完成) 1. 编写一个通用函数,该函数可以实现判断:一个含有五位数字的整数是否是回文数。回文数的含义是从左向右与从右向左看,数是相同的。如:23732是回文数,而23564则不是。编写主程序调用该函数实现求所有5位数字中满足条件的数的个数。 #include int Judge(int num) { int w,q,b,s,g; w=num/10000; q=num%10000/1000; s=(num%100)/10; g=num%10; if((w==g)&&(q==s)) return 1; else return 0; } void main() { int count=0; int i; for(i=10000;i<=99999;i++) if(Judge(i)) count++; printf("%d\n",count); } 2.编写一个通用函数,该函数可以实现对数值型数组的倒序。倒序的含义是把数组的元素值前后颠倒。例数组:20,19,18,15,13,10倒序的结果为:10,13,15,18,19,20。编写主程序,数组初始化方式不限,并输出,然后调用该函数实现倒序后再输出倒序的结果。#include #define N 6 void Transfer(double *b,int n) { double temp; double *i=b; double *j=b+n-1; while(j>i) { temp=*i; *i=*j; *j=temp; i++;

j--; } } void main() { double array[N]={20,19,18,15,13,10}; int i; for(i=0;i #include double Cal(double *p,int n) { int i,j; double sum=0; for(i=0;i

相关文档