当前课程知识点:程序设计基础(下) > 拓展学习 > C++的常见错误 > C++的常见错误
C++的常见错误
在程序开发过程中,调试必不可少。所谓“三分编程七分调试”,说明了程序调试的工作量和重要性。C++程序的错误可以分为两类:即语法错误和逻辑错误
(1)语法错误
比如,分号“;”是每条语句的结束标志,在C++语句后忘记写“;”就是语法错误,它是C++语言初学者最容易出现和出现最多的错误。有语法错误的程序是无法通过编译生成目标代码的,用户可以通过编译器的提示信息来修改语法错误。
(2)逻辑错误 就是用户编写的程序没有语法错误,可以运行,但运行的结果不正确或不是所期望的。例如:程序是要求两个数的和,应该写成 z=x+y; 由于由于粗心写成了 z=x-y; 这就是逻辑错误。
逻辑错误的产生很少是由于粗心,更多的是由于算法本身就不正确。编译器是发现不了程序中的逻辑错误的。在大部分情况下,用户需要跟踪程序的运行过程才能发现程序中逻辑错误,找出算法的错误,这是最不容易修改的。用户最常见就是Windows操作系统经常发布补丁程序,发行补丁程序就是要修改之前没有发现的逻辑错误。
这里将初学程序设计时容易犯的错误列举如下,提醒初学者加以注意。
l 在源码中遗失“;”,例如:
int main()
{
int n=100 //缺少“;”
cout<<n<<endl;
return 0;
}
编译器显示的相应错误信息是:
error C2146: syntax error : missing ';' before identifier 'cout'
l 在程序中使用中文标示符,如将英文”;”错误输入成了”;”。例如:
int main()
{
int n=100; //写成了中文“;”
cout<<n<<endl;
return 0;
}
编译器显示的相应错误信息是:
error C2065: ';' : undeclared identifier
在C++中,除程序注释和字符串中的文字可以采用中文外,其余字符都要求使用英文。
l 使用了未定义的变量。例如:
int main()
{
n=100 ;
cout<<n<<endl;
return 0;
}
编译器显示的相应错误信息是:
error C2065: 'n' : undeclared identifier
程序中变量n没有定义就赋值。C++程序中的所有变量必须“先定义、后使用”,定义就是说明变量的类型,系统为其分配相应的存储空间。C++是强类型语言,即使用数据之前,要声明数据的类型,其好处是编译器能够检查出数据类型方面的错误。
l 使用变量名时,忽视了大写字母与小写字母的区别。例如:
int main()
{
int n=100;
cout<<N<<endl;
return 0;
}
编译器显示的相应错误信息是:
error C2065: 'N' : undeclared identifier
定义了变量n,使用时写成N,实际上,n和N表示两个不同的变量。
l 缺少宏包含编译预处理命令“#include <iostream>”和“using namespace std;”,则cout、cin、endl、<<和>>等在命名空间中定义的符号和标示符无法使用。
编译器显示的相应错误信息是:
error C2065: 'cout' : undeclared identifier
l 对于流操作的方向搞错是一个普遍错误,即在使用输入输出流的时候错误使用了标示符“>>”和“<<”。
例如:cout>>a;
cin<<a;
l 忽视了字符与字符串的区别。例如:
char ch;
ch="A";
编译器显示的相应错误信息是:
error C2440: '=' : cannot convert from 'char [2]' to 'char'
ch是字符型变量,只能存放1个字符,而"A"是字符串,它包含2个字符('A'和'\0')。应改为“ch='A';”。
l 不该加分号的地方加了分号。例如:
if(a>b); // 这里不能加分号
cout<<a<<endl;
else
cout<<b<<endl;
编译器显示的相应错误信息是:
error C2181: illegal else without matching if
l ()括号没有成对出现。当使用多层括号时,要认真检查左括号与右括号是否成对。例如:
char c=1;
while((c=getchar() !='#' ) // 少1个右括号
cout<<c;
编译器显示的相应错误信息是:
error C2146: syntax error : missing ')' before identifier 'cout'
l {}没有成对出现。
例如:
int main()
{
int n=100;
cout<<n<<endl;
return 0;
//缺少右花括号
编译器显示的相应错误信息是:
fatal error C1075: end of file found before the left brace '{' at '源文件(行数)' was matched
例如:
void main()
//缺少左花括号
int n=100;
cout<<n<<endl;
}
编译器显示的相应错误信息是:
error C2144: syntax error : 'int' should be preceded by ';'
error C2143: syntax error : missing ';' before '<<'
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C2059: syntax error : 'return'
error C2059: syntax error : '}'
error C2143: syntax error : missing ';' before '}'
error C2059: syntax error : '}'
虽然出现了7条错误信息,其实就是由于缺少了一个左花括号。
当使用多层花括号时,更要认真检查左花括号与右花括号是否成对。
l 定义的变量类型与使用不对应。
例如:
int i;
float p=3.14;
i=p;
编译器显示的相应错误信息是:
warning C4305: 'initializing' : truncation from 'double' to 'float'
warning C4244: '=' : conversion from 'float' to 'int', possible loss of data
将变量p声明为float类型,但实际赋给了一个double类型的值,将变量i声明为int类型,但实际赋给了一个float类型的值。这类错误是警告错误,表示可能会对程序产生影响。可根据实际情况进行修改或忽略此错误。
l 变量在赋值之前就使用。例如:
int a, b, c;
c=a+b;
cin>>a>>b;
cout<<c;
编译器显示的相应错误信息是:
warning C4700: uninitialized local variable 'a' used
warning C4700: uninitialized local variable 'b' used
这类错误虽然也是警告错误,但会对程序产生影响,必须改正。初学者产生这个错误的主要原因是没有理解程序的执行过程。
修改后的程序是:
int a, b, c;
cin>>a>>b;
c=a+b;
cout<<c;
l 对二维数组的定义和引用的方法不对。例如:
int x[3, 4];
…
cin>>x[0, 0];
二维数组或多维数组的每一维的下标都必须单独用一对方括号括起来。应改为:
int x[3][ 4];
…
cin>>x[0][ 0];
l 不知道字符数组与字符指针的区别。请看下面的程序:
int main()
{
char str[10]; // 第3行
str="Computer"; // 第4行
cout<<str<<endl;
return 0;
}
编译器显示的相应错误信息是:
error C2440: '=' : cannot convert from 'char [9]' to 'char [10]'
其中str是数组名,代表数组的首地址,它是一个常量,不能给它赋值,因此第4行是错误的。如果把第3行改为“char *str;”,即str是指向字符数据的指针,则第4行是正确的,即将字符串的首地址赋给指针变量str。
l 在函数定义时,()后面使用分号。例如:
void Chang(); //使用了分号
{
……
}
编译器显示的相应错误信息是:
error C2447: '{' : missing function header (old-style formal list?)
l 函数声明/定义/调用参数个数不匹配。例如:
void Chang(int a,int b, float c)
{
……
}
int main()
{
……
Chang(3,4);
……
}
编译器显示的相应错误信息是:
error C2660: 'Chang' : function does not take 2 parameters
l 被调用的函数在调用位置之后定义,而又没有在调用之前进行函数声明。例如:
void main()
{
……
Chang(3,4);
……
}
void Chang(int a,int b, float c)
{
……
}
编译器显示的相应错误信息是:
error C3861: 'Chang': identifier not found
error C2365: 'Chang' : redefinition; previous definition was 'formerly unknown identifier'
改正方法有两种:一是在调用Chang函数的语句或函数前添加函数声明语句void “Chang(int a,int b, float c)”;二是把函数Chang的定义移到主函数之前或调用该函数的语句前。
l 将最大下标等同于数组元素个数。例如:
int a[10];
for( int i=-1;i<=10;i++)
a[i]=i*i;
因为数组元素的最小下标规定为0,所以最大下标等于元素个数减1,而不是元素个数。
编译器对数组下标越界并不进行检查,不会报告错误信息。同样,下标如果小于0,编译器也不会报告错误信息。
l 使用没有明确指向的指针。例如:
int *p;
*p=100;
编译器显示的相应错误信息是:
warning C4700: uninitialized local variable 'p' used
把1000存入p指向的存储单元,而p指向何处并不知道,这样就很可能破坏程序或数据。虽然编译器给出的是警告错误,但这类错误必须改正。
l 没有进行指针的合法性检验。例如:
int main()
{
int n, *p;
cout<<"请输入数组元素个数:";
cin>>n;
p=new int[n];
……
}
这里能通过编译,但可能隐藏着难以发现的错误,如果堆内存(动态数据区)剩余空间不足,则返回一个空指针。预防发生这类问题的方法是对指针的合法性进行检验。将语句“p=new int[n];”修改为:
if((p=new int[n])==NULL)
{
cout<<"动态分配内存未成功"<<endl;
exit(1);
}
l 混用不同类型的指针。例如:
int a=5, *p1;
double b=0.5, *p2;
p1=&a;
p2=&b;
p2=p1;
编译器显示的相应错误信息是:
error C2440: '=' : cannot convert from 'int *' to 'double *'
最后一个语句试图使p2也指向a,但p2是指向double型变量的指针,不能指向整型变量。指向不同类型的指针之间的赋值必须进行强制类型转换,即此语句可以改为:
p2=(double *)p1;
l 在一个工程中包含多于一个的main函数
如果在一个源文件中包含两个或两个以上的main函数,则编译器显示的相应错误信息是:
error C2084: function 'int main(void)' already has a body
如果在不同的源文件中包含了两个或两个以上main函数,则编译器不会找到错误,但连接器由于包含多个main函数而无法将多个.obj文件连接成可执行文件,显示的相应错误信息是:
error LNK2005: _main already defined in p1.obj
l 混淆结构体类型与结构体变量的区别,为一个结构体类型赋值。例如:
struct Student
{
long num;
char name[20];
char sex;
int age;
};
void main()
{
Student.num=20051127;
}
编译器显示的相应错误信息是:
error C2143: syntax error : missing ';' before '.'
上面只定义了结构体类型struct Student,而未定义结构体变量。不能为结构体类型赋值,只能为结构体变量的各成员赋值。应改为:
struct Student
{
long num;
char name[20];
char sex;
int age;
};
void main()
{
Student stu;
stu.num=20051127;
}
l 类声明后没有分号。例如:
class Student
{
long num;
char name[20];
char sex;
int age;
} //此处没有加“;”号
int main()
{
Student stu;
……
}
编译器显示的相应错误信息是:
error C2628: 'Student' followed by 'int' is illegal (did you forget a ';'?)
error C3874: return type of 'main' should be 'int' instead of 'Student'
error C2664: 'Student::Student(const Student &)' : cannot convert parameter 1 from 'int' to 'const Student &'
虽然显示3条错误,其实仅仅是因为在声明类Student的最后少了分号。
l 混淆类类型与对象的区别,为一个类类型赋值。例如:
class Student
{
public:
char m_number[7];
char m_name[10];
……
};
int main()
{
strcpy(Student.m_number, "0801011");
strcpy(Student.m_name, "刘明");
……
}
编译器显示的相应错误信息是:
warning C4832: token '.' is illegal after UDT 'Student'
error C2275: 'Student' : illegal use of this type as an expression
上面只定义了类类型 class Student,而未定义该类形的变量。不能为类类型赋值,只能为类类型的变量——对象的各成员赋值。应改为:
class Student
{
public:
char m_number[7];
char m_name[10];
……
};
int main()
{
Student stu;
strcpy(stu.m_num, "0801011");
strcpy(stu.m_name, "刘明");
……
}
l 在类作用域外直接访问类的私有成员
例如:
class Student
{
char m_name[10];
……
};
int main()
{
Student stu;
strcpy(stu.m_name,"刘明");
……
}
编译器显示的相应错误信息是:
error C2248: 'Student::m_name' : cannot access private member declared in class 'Student'
l 调用类的无参成员函数,不加小括号。例如:
class Student
{
char m_number[8];
char m_name[10];
public:
void display()
{
cout<<m_number<<" "<<m_name;
}
……
};
void main()
{
Student stu;
stu.display; //函数调用缺少小括号
……
}
编译器显示的相应错误信息是:
error C3867: 'Student::display': function call missing argument list; use '&Student::display' to create a pointer to member
函数调用的形式是:函数名(实参表),对无参函数调用时不能缺少小括号,即函数调用形式是:函数名()。
l 调用构造函数。例如:
#include<iostream>
#include<string>
using namespace std;
class Student
{
string m_number;
string m_name;
public:
Student(char * number, char * name )
{
m_number=number;
m_name=name;
}
……
};
int main()
{
Student stu("0801011","刘明");
stu.Student("0801012","姚翔"); //试图调用构造函数
……
}
编译器显示的相应错误信息是:
error C2274: 'function-style cast' : illegal as right side of '.' operator
类的构造函数在创建对象时,由编译系统自动调用一次。当一个对象已经产生,只能通过其它方法给对象的数据成员赋值,不能再次调用构造函数来修改对象的数据成员。
l 缺少无参构造函数。例如:
#include<iostream>
#include<string>
using namespace std;
class Student
{
string m_number;
string m_name;
public:
Student(char * number, char * name )
{
m_number=number;
m_name=name;
}
……
};
int main()
{ Student stu1("0801011","刘明"), stu2; //声明stu2对象时没有提供实参
……
}
编译器显示的相应错误信息是:
error C2512: 'Student' : no appropriate default constructor available
此时,对象stu2需要无参的构造函数来创建。但由于Student类中已经定义了一个有两个参数的构造函数,系统提供的默认构造函数不再起作用。所以即使不对对象的成员进行任何初始化,也需要在Student类中再定义一个无参的构造函数:
Student(){}
常见的逻辑错误有运算符使用不正确、变量或表达式的值超出数据类型所能表示的数值范围、语句的先后顺序不对、条件语句的边界值不正确、循环语句的的初值与终值有误等。发生逻辑错误的程序是不会产生错误信息,需要程序设计者细心地分析阅读程序,并进行程序调试来发现逻辑错误。
高超的程序员在开发软件时,也会犯错误,甚至是犯低级的语法错误,关键是能够快速找到错误、改正错误。初学者是在发生错误时,由于不熟悉程序调试工具和调试技巧,往往不知道发生了什么错误,是由什么引起的,也就不知道如何修改错误。调试程序是一个艰苦、心细、又有技巧的事,只有经常上机多调试程序,才能不断地积累经验,才能提高程序调试技能。一些调试技巧如:监视循环体时,只要监视循环开始的几次和最后几次循环和循环体内的条件语句成立与否时的各变量的值,就可以知道该循环是否有逻辑错误;监视选择语句时关键是看条件成立与否的分界值。
下面是初学者常犯的一些逻辑错误。
l 变量或表达式的值超出数据类型所能表示的数值范围。例如:
int a, b;
a=200000; b=100000;
cout<<a*b<<endl;
输出结果是一个负数(或者根本得不到计算结果),发声逻辑错误。原因是表达式a*b的值已超出int型数据的范围。
l 误把赋值号“=”作等号“==”使用。例如:
if(a=b)
cout<<"a equal to b"<<endl;
该语句是要判断变量a和变量b是否相等,但是误用了“=”后,只要b不等于0,赋值表达式a=b的值就不等于0,因此满足条件,无论a是否等于b,都输出a equal to b,发生逻辑错误。
l 不该加分号的地方加了分号。例如:
for (int i=1;i<=10;i++); // 这里不能加分号
cout<<i<<endl;
该语句是要向屏幕输出1——10,但由于误加了一个分号,程序只向屏幕输出一个11,发生逻辑错误。
l 复合语句没有加花括号。例如,试图通过下面的for循环一边为数组元素赋值,一边累加求元素的和:
for(i=1; i<=100; i++)
s[i]=i*i;
sum=sum+s[i];
由于语句“sum=sum+s[i];”未在循环体内,所以sum保存的是数组元素s[101]的值,没有实现将s[1]到s[100]的和累加到变量sum中的功能,发生逻辑错误。就是因为循环体中的两条语句没有加花括号。此语句应修改为:
for(i=1; i<=100; i++)
{
s[i]=i*i;
sum=sum+s[i];
}
l 试图用数组名代表数组中的全部元素。例如
int a[5]={1,2,3,4,5};
cout<<a;
数组名a表示数组的首地址,输出的是数组的首地址,而不是数组中的全部元素。
l 对switch语句中case作用的误解。例如,下面程序是将输入的百分制成绩按规定分为5个等级:90~100分为A;80~89分为B;70~79分为C;60~69分为D;0~59分为E。
int main()
{
int s, g;
cout<<"input s(0~100):";
cin>>s;
g=s/10;
switch(g)
{
case 10:
case 9: cout<<"A"<<endl; // 第10行
case 8: cout<<"B"<<endl;
case 7: cout<<"C"<<endl;
case 6: cout<<"D"<<endl; // 第13行
default: cout<<"E"<<endl;
}
return 0;
}
执行该程序时,输入85,输出结果是BCDE。为什么会得到4个等级呢?switch语句的执行过程是:当变量g的值与某一个case后面的常量相等时,就执行该case后面的语句,执行完后继续执行下一个case后面的语句。“case 常量:”只起语句标号的作用,一旦找到入口标号开始执行,就不再进行判断,一直执行到switch语句结束。因此,对此程序应做如下修改:在第10行至第13行后各添加一条break语句,跳出switch。例如:
case 9: cout<<"A"<<endl; break;
l 实参表达式求值规则。在C++环境下进行函数调用时,系统对实参表达式按从右到左的顺序进行求值。在阅读程序或编写程序时务必注意这一特点。例如:函数fun()的功能是比较两个整数的大小。当x大于y时返回1;x等于y时返回0;x小于y时返回-1。执行下面程序后输出结果是什么?
int fun(int x, int y)
{
int z;
if(x>y)
z=1;
else if(x==y)
z=0;
else
z= -1;
return z;
}
int main()
{
int a=1, b;
b=fun(a, ++a);
cout<<b<<endl;
return 0;
}
在主函数的函数调用fun(a,++a)中,有两个实参表达式a和++a。按从右到左的求值规则,两个表达式的值相等(都是2),所以函数返回值是0,即输出结果是0。如果从左到右求值,则函数返回值为-1。
l 误认为实参的值随形参值的改变而改变。例如,下面程序是想通过调用函数swap()交换a和b的值:
void swap(int x, int y); // 函数声明
int main()
{
int a, b;
a=80;
b=60;
swap(a, b);
cout<<"a="<<a<<','<<"b="<<b<<endl;
return 0;
}
void swap(int x, int y)
{
int t;
t=x; x=y; y=t;
}
程序输出结果是:a=80,b=60
实参a和b的值传递给形参x和y,虽然交换了x和y的值,但实参的值并没有交换,就是说,实参的值不随形参值的改变而改变,这就是所说的单向传值。
l 忽视数值计算中的舍入误差可能引起严重错误。在计算机中,实数用浮点数表示。因为只能用有限个二进制位存放实数的尾数,而这有限位分别取0或1所组成的状态数是有限的,一个状态表示一个实数,所以能够精确表示的实数只是一个有限数集,该数集以外的实数只能按“0舍1入”的原则保留有限位,多余的位则被舍去,于是产生了舍入误差。例如,小数0.1、0.3、0.48等化成二进制小数都有无穷多位,只能将它们表示成近似数。这些近似数所参与的每一步运算都会引入舍入误差,经过多次运算,误差的积累就会严重影响计算结果,甚至使计算结果失去意义。
例如,计算下面表达式的值:
程序如下:
int main()
{
float r=1.0, s=0.0, t;
int n, i;
cout<<"input n:";
cin>>n;
for(i=1; i<=n; i++)
{
r=r/3.0;
s=s+r;
}
t=(1.0-2.0*s)/r;
cout<<setprecision(5)<<setiosflags(ios::fixed);
cout<<t<<endl;
return 0;
}
程序运行结果是:n=20 时,t=207.82857;n=30 时,t=12272070.00000。可见,舍入误差的影响有多大!单从程序设计角度来看,这种误差是难以发现的。
显然,完全消除舍入误差是不可能的,我们只能设法减小舍入误差。减小舍入误差的一个有效方法是使用双精度运算,即将程序中的“float r=1.0, s=0.0, t;”语句改为:
doulbe r=1.0, s=0.0, t;
这时,程序运行结果是: n=20时,t=1.00000(t的理论值为1);n=30时,t=1.05149。可见,这时舍入误差的影响很小,可以忽略不计。
此外,简化计算步骤、减少计算次数都能有效地减小舍入误差的影响。
t的理论值推导:
在以上公式中,方括号内是一个等比级数,等比级数前n项的和sn为:
其中a1为首项,q为公比。本题中a1=1/3,q=1/3,所以有
于是得到:
-C++的常见错误
--C++的常见错误
-MFC入门
--MFC入门
-STL及使用示例
--STL及使用示例
-算法设计与算法分析基础
-算法设计基本方法与策略基础
-计算机前沿问题思考
-QT编程入门
--QT编程入门
-C++中的string类
-面向对象方法应用实例
-继承与多态应用实例
-排序算法
--排序算法
-C++常见问题汇总
-学习感悟(1)
--学习感悟(1)
-学习感悟(2)
--学习感悟(2)
-学习感悟(3)
--学习感悟(3)
-学习感悟(4)
--学习感悟(4)
-1.1面向对象方法的基本概念
-1.1面向对象方法的基本概念--作业
-1.2 C++中类的定义
-1.2 C++中类的定义
-1.3C++中对象的定义和访问
-1.3C++中对象的定义和访问--作业
-1.4类成员的访问控制
-1.4类成员的访问控制--作业
-1.5析构函数
--1.5析构函数
-1.5析构函数--作业
-1.6拷贝构造函数
-1.6拷贝构造函数--作业
-1.7 类声明与类实现的分离
-1.7 类声明与类实现的分离--作业
-1.8类的静态成员
-1.8类的静态成员--作业
-1.9类的常量成员
-1.9类的常量成员--作业
-1.10this指针
-1.10this指针--作业
-1.11类的友元
--1.11类的友元
-1.11类的友元--作业
-1.12类的对象成员
-1.12类的对象成员--作业
-1.13自定义类的运算符重载
-1.13自定义类的运算符重载--作业
-2.1 派生类的定义和继承方式
-2.1 派生类的定义和继承方式--作业
-2.2 派生类中函数的重定义
-2.2 派生类中函数的重定义--作业
-2.3派生类的构造函数和析构函数
-2.3派生类的构造函数和析构函数--作业
-2.4多继承
--2.4多继承
-2.4多继承--作业
-2.5多态
--2.5多态
-2.5多态--作业
-2.6抽象类
--2.6抽象类
-2.6抽象类--作业
-3.1cout和cin对象及插入和提取运算符
-3.1cout和cin对象及插入和提取运算符--作业
-3.2 使用put和get函数进行标准输出和输入
-3.3使用getline函数进行标准输入
-3.3使用getline函数进行标准输入--作业
-3.4 文件流对象
-3.5文件流对象及插入和提取运算符
-3.5文件流对象及插入和提取运算符--作业
-3.6文件流对象及put、get和getline函数
-3.7按数据块进行输入输出
-3.8按数据块进行输入输出实例
-3.8按数据块进行输入输出实例--作业
-3.9文件的随机读写
-3.9文件的随机读写--作业
-3.10用户自定义数据类型的输入输出
-3.10用户自定义数据类型的输入输出--作业
-4.1函数模板
--4.1函数模板
-4.1函数模板--作业
-4.2类模板
--4.2类模板
-4.2类模板--作业
-5.1数据结构基本概念(一)
-第五章 概论--5.1数据结构基本概念(一)
-5.2数据结构基本概念(二)
-5.2数据结构基本概念(二)--作业
-6.1线性表及其抽象数据类型
-6.1线性表及其抽象数据类型--作业
-6.2顺序表类模板
-6.2顺序表类模板--作业
-6.3顺序表的实现
-6.3顺序表的实现--作业
-6.4简单数据元素顺序表的应用问题
-6.4简单数据元素顺序表的应用问题--作业
-6.5复杂数据元素顺序表的应用问题
-6.5复杂数据元素顺序表的应用问题--作业
-6.6单向链表及其类模板
-6.6单向链表及其类模板--作业
-6.7单项链表的实现(一)
-6.7单项链表的实现(一)--作业
-6.8单项链表的实现(二)
-6.8单项链表的实现(二)--作业
-6.9单向链表的应用
-6.10循环链表及双向链表
-7.1栈及顺序栈
--7.1栈及顺序栈
-7.1栈及顺序栈--作业
-7.2 顺序栈的实现
-7.2 顺序栈的实现--作业
-7.3顺序栈的应用
-7.4 链接栈及其实现
-7.4 链接栈及其实现--作业
-7.5队列及其顺序存储
-7.5队列及其顺序存储--作业
-7.6 顺序循环队列的实现
-7.6 顺序循环队列的实现--作业
-7.7顺序循环队列的应用
-7.8链接队列及其实现
-7.8链接队列及其实现--作业
-8.1树的基本概念
-8.1树的基本概念--作业
-8.2二叉树及其性质
-8.2二叉树及其性质--作业
-8.3二叉树的抽象数据类型和顺序表示
-8.3二叉树的抽象数据类型和顺序表示--作业
-8.4二叉树的链式表示
-第八章 树和二叉树--8.4二叉树的链式表示
-8.5二叉链表的实现(一)
-8.6二叉链表的实现(二)先序和中序遍历
-8.6二叉链表的实现(二)先序和中序遍历--作业
-8.7二叉链表的实现(三)后序和逐层遍历
-8.7二叉链表的实现(三)后序和逐层遍历--作业
-8.8二叉链表的实现(四)
-8.9 二叉排序树
-8.10哈夫曼树和哈夫曼编码
-9.1图的基本概念及其特征
-9.1图的基本概念及其特征--作业
-9.2图的抽象数据类型和表示方式
--Video
-9.2图的抽象数据类型和表示方式--作业
-9.3图的邻接矩阵表示法的实现
-9.3图的邻接矩阵表示法的实现--作业
-9.4图的广度优先遍历
--Video
-9.4图的广度优先遍历--作业
-9.5图的深度优先遍历
-9.5图的深度优先遍历--作业
-9.6图的应用
--9.6 图的应用
-面向对象方法
--面向对象例题讲解
--友元和运算符重载
--对象成员
-继承与多态
--多重继承
--虚函数
--抽象类和纯虚函数
-输入输出流
--输入输出流操作
-模板
--函数模板
--类模板