第一篇:C++ 面试题(服务器编程、网络编程方便)
C++ 面试题-专注于服务器编程、网络编程 C++ 面试题
1.类成员指针
Class test
{
Public:
Int fun(int)const;
Int fun(int);
Static int fun(int);
Int iTemp;
}
1.1非静态成员函数指针定义:
int(Test::*pFun)(int)= &Test::fun;
int(Test::*pFunConst)(int)const = &Test::fun;
使用:
Test a;
const Test b;
(a.*pFun)(2)或(a.*pFunConst)(2);
(b.*pFunConst)(2);
不能用(b.*pFun)(2);
1.2非静态成员变量int Test::*pInt = &Test::iTemp;
(a.*pInt)= 3;
1.3静态成员函数指针int(*pFun)()= &Test::fun;
或 int(*pFun)()= Test::fun;都正确;(注:定义无域操作符)使用:
(*pFun)()或 pFun()都正确;
2.非成员函数指针和静态成员函数一致。
3.非成员函数和静态成员函数上不允许修饰符。例如 void fun()const;void fun()volatile;但非静态 成员函数允许const、volatile等修饰符。
4.变量修饰符
auto:指定数据存储在栈中。局部变量默认为auto。该修饰符不能用于成员变量和全局变量。
static: 局部变量表示存储在静态空间,全局变量表示不允许外部引用。
volatile:表示该变量可能随时改变,不要做任何假设优化。
mutale: 去除成员变量的const属性。
extern:全局变量默认为extern属性,表示可被外部引用,此时与static相对。
extern int a =2;表示定义一个可被外部引用的变量。
extern int a;表示引用外部变量。
5.数据类型隐式转换
短数据->长数据(eg: float-> double)
有符号->无符号(eg: int-> unsigned int)PS: 所以 int(-1)>unsigned int(1);
低精度->高精度(eg: int-> float)
6.memcpy 有“防重叠”覆盖机制,strcpy 没有。
7.float表示
共计32位,折合4字节
由最高到最低位分别是第31、30、29、……、0位
31位是 符号位,1表示该数为负,0反之。
30-23位,一共8位是指数位。
22-0位,一共23位是尾数位。
每 8位分为一组,分成4组,分别是A组、B组、C组、D组。
每一组是一个字节,在内存中逆序存储,即:DCBA
8.不能在类的声明中初始化类常量,而只能在构造函数初始化列表来初始化
9.类中的枚举常量不占用对象的存储空间
10.有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。
11.赋值函数,应当用“引用传递”的方式返回String 对象。如果用“值传递”的方式,虽然功能仍然正确,但由于return 语句要把 *this 拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。
12.对于非内部数据类型的对象而言,光用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。如果用free 释放“new 创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete 释放“malloc 申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。
13.如果用new 创建对象数组,那么只能使用对象的无参数构造函数,delete时如果对象没有析构函数,则delete和delete[]是功能相同的。
14.只能靠参数而不能靠返回值类型的不同来区分重载函数。编译器根据参数为每个重载函数产生不同的内部标识符。并不是两个函数的名字相同就能构成重载。全局函 数和类的成员函数同名不算重载,因为函数的作用域不同。
15.关键字inline
必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。定义在类声明之中的成员函数将自动地成为内联函数。
以下情况不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体 内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
16.只有静态常量整型数据成员才可以在类中初始化,普通成员只能在初始化列表或函数内初始化,常量成员只能在初始化列表。成员对象初始化的次序完全不受它们在 初始化表中次序的影响,只由成员对象在类中声明的次序决定。
17.拷贝构造函数和赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。
18.不想编写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的,则只需将拷贝构造函数和赋值函数声明为私有函数,不用编写代码。
19.“const T”和“T const”总是都被允许的,而且是等效的。
注意在常量指针(const pointer)中,“const”永远出现在“*”之后。
例如:
int *const p1 = q;//指向int 变量的常量指针
int const* p2 = q;//指向int 常量的指针
const int* p3 = q;//指向int 常量的指针
20.一个常见的微妙问题是,一个函数风格的宏并不遵守函数参数传递的规则。
21.没有引用数组,但可有指向数组的引用,并且保留数组的边界。
22.左值拥有保存值的位置,而右值则没有相关地址,只是简单值。
23.负索引是可以的,如p[-2]或(-2)[p]都是可以的(p必然不能是数组名),但必须保持不越界。
sum=p()+q()+r();不能保证p(),q(),r()调用的顺序。
逗号运算符“,”可以保证求值顺序.result=(p(),q(),r());是先求p(),q(),然后将r()赋给result
24.在if的条件里声明变量,且可在真假分支里面使用。
const int * const * p;p是个指针,指向常指针的,常指针指向一个常量int。
25.不能用空白初始化列表来规定默认的对象初始化.class a;
a A();//会警告,看起来像函数声明
a *p=new a();//ok
a *p=new a;//ok
26.可以写一句只有数字的代码,如1234;(void)0;可以编译执行,相当于nop。
27.给函数指针赋值时可以对函数名取地址也可以不取,通过函数指针调用函数时可以用*也可不用。
28.static_cast可以转换基本数据类型(int->char)、void*和有类型指针、基类和派生类指针的转换(多重继承也行,它可重新计算偏移地址),但是不能转换如(int*->char*等)。
29.dynamic_cast主要用于执行“安全的向下转型”,reinterpret_cast可执行任何转换,const_cast执行去 const转换。
30.将取地址运算符用到完全限定的类成员名(包括变量和函数),就能获得指向成员的地址。使用形式为“X::*”来声明一个指向类X成员的指针。注意声明成员 函数指针的时候不能像普通函数指针可以省略&或*的使用,但静态成员函数则除外,它和普通函数一致。成员指针和普通指针不一样,并非指向一个内存 区域,而是相当于一个结构的偏移量,当它和具体的对象结合就能指向特定对象的特定成员。
31.当把派生类对象赋给基类对象的时候会产生切割现象,即针对派生类的数据和行为将产生切割。
32.多维数组的第1个元素是数组而非普通类型。
33.在含有单参数构造函数的类中注意隐式转换。如String s=“Hello”;
34.函数对象是重载函数调用运算符的类对象。
35.引用需要用左值进行初始化,但指向常量的引用除外,编译器将创建一个临时左值。如const int c=12;//ok 一般情况下编译器产生的临时对象的生命期在它所在的最大表达式范围内,但用临时对象初始化常量对象的引用时会让编译器保证临时对象和引用生命周期一样。
36.可以将基类的成员指针(变量或函数)安全的转换为指向派生类成员的指针,但反之则不安全。
37.函数参数的传递是采用拷贝构造函数而非赋值操作。对未初始化的对象赋值可能会出现意外,如类中含有未初始化指针。
38.声明但不定义私有的拷贝构造和赋值运算将会关闭类的复制操作。并且赋值运算、拷贝构造函数和析构函数不会被继承,对派生类重载赋值运算时需要调用基类的赋 值运算。
39.在构造函数里对成员变量初始化,比较好的方式是使用初始化列表。在初始化列表中静态成员和数组不能被初始化。
类的初始化顺序是虚拟基类的成员->非虚基类成员->类自身成员,和初始化列表的顺序无关。含有虚拟基类和不含的类在成员布局上不一样,含有虚拟基类的类将虚拟基类的数据放在最后面。另外如B:virtual A,C:virtual A,D:B,C;(均是虚继承)则D的构造函数将对A初始化一次(即使在初始化列表没有显式初始化A),B,C将不再对A初始化。
所有静态数据(全局变量和静态存储变量)在使用前如未初始化其值都为0.全局变量可以存储在静态初始化区和未初始化区。
40.RVO返回值优化,是指在函数返回中执行拷贝初始化到直接初始化(使用带非对象参数的构造函数)的转换,NRV和RVO类似,但使用命名局部变量来保存返 回值。p160
41.重载、覆盖和隐藏的区别
重载的特征:在同一个类,函数名相同,参数不同,virtual可有可无。
覆盖的特征:在两个类(基类和派生类),函数名和参数都相同,且必须有virtual关键字。
隐藏的特征:基类函数名和派生类函数名相同参数不同,且不管是否有关键字。或函数名、参数均相同,但基类 函数没有virtual(有的话就是覆盖)。
不能覆盖而只能隐藏基类非虚函数。
42.相同类型的所有对象公用一个虚函数表,在单继承下不管有多少个虚函数都只有一个虚函数表指针。覆盖就是在为派生类构造虚函数表时用派生类的函数地址替换基 类成员函数地址的过程。
43.使用常量类成员可能在对类对象赋值的时候产生问题。
44.有时候我们可能会看到 if(NULL == p)这样古怪的格式。不是程序写错了,是程序员为了防止将 if(p == NULL)误写成 if(p = NULL),而有意把p 和NULL 颠倒。
第二篇:C++编程心得
紧张忙碌的一周过去了,在这个星期里我们小组胜利地将我们选定的课题研究出来,我为我的小组骄傲,也为自己骄傲。
我们的题目是银行定期存款管理系统,在编程过程中,我预订的任务是学习并掌握MFC,并将其运用到我们的程序中。学习MFC的过程是我认为非常痛苦的事,因为完全都是自学,并且因为图书馆中的相关书籍全部被借阅出去,所以没有太多的自学资料,因此虽然费了很大的精力,但仍是无果。经过了很多的小组讨论和自己的心理斗争后最终决定放弃MFC而将自己能力以内的程序尽可能地优化,就这样,我开始在半路阶段加入了程序优化的工作,一遍遍的调试程序,遇到某个攻坚问题,几个人一起在网上查询,一起想解决的办法,就这样,我们团结地将我们现在认为将近完美的程序呈现给老师和同学。
一开始学习MFC时,我总是不能很好的转换思维,不能将自己以前的编程思想和以前的一些不良编程习惯甩掉,因此很多的程序都联系不起来,反而是自己学会了好多的零碎的片面却无法将程序大楼竖起来。在困惑很久后,我选择了询问老师,老师耐心地将MFC的思维方式讲给了我听,这让我受益匪浅,虽然我最后没能将MFC成功的做出来,但我认为我从MFC中才直接地感受到了C++中面向对象编程的封装性。以前用C++编程时总是将类中的变量随便规定属性,甚至在编程的过程中突然需要加一个变量,为了省事直接将其放于public中,在学习了解MFC后,深刻地感受到自己以前的编程习惯是多么的不科学,而那些习惯又是多么的重要。
我们这一次是以团队的形式来做这个项目,并且作为这个小组的组长,我感觉自己的压力很大,因为团队合作不仅讲求整体的团结而且为了团队工作的高效又要有明确的分工。在我们的小组工作中每个人都一刻不放松自己手里的工作,而一些时候我们又配合地很密切,尤其是当我们遇到程序的瓶颈时,我们都是在宿舍里你一句我一句地出谋划策,在遇到一个提取时间的程序时,大家纷纷查询各种辅导书,上网查各种资料,在遇到定期存款支取问题时甚至打电话给家长,寻求“场外支持”,种种的配合都给我们这个部门协调的运作提供了动力。我感谢我的团队,他们让我有一种很舒服的做事情的感觉。
最终决定放弃MFC,我承认自己有一种如释重负的感觉,但是也有一丝惭愧,无论老师要求与否,既然自己决定了就应该走下去,但是我最后退却了,感觉很遗憾也很羞愧,因为我认为对自己降低要求就是放纵自己堕落。
短学期实习,我着实收获了很多,不仅仅是知识上,而且是思考事情方法,我感激这次的实习,也感谢给予我们很多帮助的老师,更感谢我团结的团队。
第三篇:C++编程思想读后感
Unit One对象的演化
oop技术能够很容易地将大量问题归纳为一个简单的解,这一发现产生了大量的oop语言,其中最著名的是Smalltalk—C++之前最成功的oop语言。
继承表示了基本类型和派生类型之间的相似性,程序员创建一个基本类型以描述系统中一些对象的思想核心。由这个基本类型派生出其他类型,表达了认识该核心的不同途径。
早捆绑意味着编译器对特定的函数名产生调用,而连接器确定调用执行代码的绝对地址。对于。Oop采用动态绑定。当给对象发送消息时,在程序运行之前不去确定被调用的代码。编译器保证这个被调用的函数存在,并完成参数和返回值的类型检查,但是它不知道将执行的准确代码。为了实现晚捆绑,编译器在真正调用的地方插入一段特殊的二进制代码。通过使用存放在对象自身中的信息,这段代码在运行时计算被调用函数的地址。这样,每个对象就能根据一个指针的内容有不同的行为。当一个对象接收到消息时,它根据这个消息判断应当做什么。
程序员可以用关键字v i r t u a l表明他希望某个函数有晚捆绑的灵活性,而并不需要懂得v i r t u a l的使用机制。没有它,就不能用C + +做面向对象的程序设计。Vi r t u a l函数(虚函数)表示允许在相同家族中的类有不同的行为。这些不同是引起多态行为的原因。
用C 语言编写的过程程序就是一些数据定义和函数调用。要理解这种程序的含义,程序员必须掌握函数调用和函数实现的本身。这就是过程程序需要中间表示的原因。中间表示容易引起混淆,因为中间表示的表述是原始的,更偏向于计算机,而不偏向于所解决的问题。
通常,面向对象程序需要较少的代码,因为问题中的许多部分都可以用已存在的库代码。
C+ +成功的原因是经济上的:转变到O O P需要代价,而转变到C + +所花的代价较小。尽可能地为程序员提供最大便利。
为C + +堵塞了C语言中的一些漏洞,并提供更好的类型检查和编译时的分析。程序员必须先说明函数,使编译器能检查它们的使用情况。预处理器虚拟删除值替换和宏,这就减少了查找疵点的困难。C + +有一个性能,称为r e f e r e n c e s(引用),它允许对函数参数和返回值的地址进行更方便的处理。函数重载改进了对名字的处理,使程序员能对不同的函数使用相同的名字。另外,名字空间也加强了名字的控制。许多性能使C的更安全。面向对象的C + +程序的速度与用C写的程序速度相差在± 1 0 %之内,而且常常更接近。用O O P方法设计的程序可能比C的对应版本更有效。
c+ +的主要目标是让程序员能更容易地使用库,这是通过将库转换为新数据类型(类)来完成的。引入一个库,就是向该语言增加一个新类型。编译器负责这个库如何使用,保证适当的初始化和清除,保证函数被正确地调用。
• 模板的源代码重用
一些重要的类型要求修改源代码以便有效地重用。模板可以自动完成对代码的修改,因而是重用库代码特别有用的工具。用模板设计的类型很容易与其他类型一起工作。因为模板对程序员隐藏了这类代码重用的复杂性,所以特别好用。
C + +的异常处理(见第1 7章的内容)保证能检查到错误并进行处理。
C语言同样有这样的限制,例如当程序超过50 000行时,名字冲突就开始成为问题。简言之,程序员用光了函
数和变量名。设计C + +的目的是为了辅助大程序设计,也就是说,去掉小程序和大程序之间复杂性的分界。
程序设计有两个原则:
1)内部原则体现在程序自身的结构中,机灵而有见解的程序员可以通过程序设计语言的表达方式了解这种内部原则。
2)外部原则体现在程序的源信息中,一般被描述为“设计文档”(不要与产品文档混淆)。
过程语言:为科学工作者使用的F O RT R A N(F O R m u l a-T R A N s l a t i o n)和为商业者使用的C O B O L
(COmmon Business-Oriented Language)。纯计算机科学中很成功的语言是L i s p(L i s t-P r o c e s s i n g),而面向数学的语言应当是A P L(A Programming L a n g u a g e)。
1.3.4 对象设计的五个阶段
1)对象发现这个阶段出现在程序的最初分析期间。可以通过寻找外部因素与界线、系统中的元素副本和最小概念单元而发现对象。如果已经有了一组类库,某些对象是很明显的。类之间的共同性(暗示了基类和继承类),可以立刻出现或在设计过程的后期出现。
2)对象装配我们在建立对象时会发现需要一些新成员,这些新成员在对象发现时期未出现过。对象的这种内部需要可能要用新类去支持它。
3)系统构造对对象的更多要求可能出现在以后阶段。随着不断的学习,我们会改进我们的对象。与系统中其它对象通讯和互相连接的需要,可能改变已有的类或要求新类。
4)系统扩充当我们向系统增添新的性能时,可能发现我们先前的设计不容易支持系统扩充。这时,我们可以重新构造部分系统,并很可能要增加新类。
5)对象重用这是对类的真正的重点测试。如果某些人试图在全新的情况下重用它,他们会发现一些缺点。当我们修改一个类以适应更新的程序时,类的一般原则将变得更清楚,直到我们有了一个真正可重用的对象。
对象开发原则
1)让特殊问题生成一个类,然后在解其他问题时让这个类生长和成熟。
2)记住,发现所需要的类,是设计系统的主要内容。如果已经有了那些类,这个项目就不困难了。
3)不要强迫自己在一开始就知道每一件事情,应当不断地学习。
4)开始编程,让一部分能够运行,这样就可以证明或反驳已生成的设计。不要害怕过程语言风格的细面条式的代码—类分割可以控制它们。坏的类不会破坏好的类。
5)尽量保持简单。具有明显用途的不太清楚的对象比很复杂的接口好。我们总能够从小的和简单的类开始,当我们对它有了较好地理解时再扩展这个类接口,但不可能简化已存在的类接口。
第2章数据抽象
库,简单地说就是一些人已经写的代码,按某种方式包装在一起。通常,最小的包是带有扩展名如L I B的文件和向编译器声明库中有什么的一个或多个头文件。连接器知道如何在L I B文件中搜索和提取相应的已编译的代码。但是,这只是提供库的一种方法。在跨越多种体系结构的平台上,例如U N I X,通常,提供库的最明智的方法是用源代码,这样在新的目标机上它能被重新编译。而在微软Wi n d o w s上,动态连接库是最明智的方法,这使得我们能够利用新发布的D D L经常修改我们的程序,我们的库函数销售商可能已经将新D D L发送给我们了。
2.1 声明与定义
“声明”向计算机介绍名字,它说,“这个名字是什么意思”。而“定义”为这个名字分配存储空间。无论涉及到变量时还是函数时含义都一样。无论在哪种情况下,编译器都在“定义”处分配存储空间。对于变量,编译器确定这个变量占多少存储单元,并在内存中产生存放它们的空间。对于函数,编译器产生代码,并为之分配存储空间。函数的存储空间中有一个由使用不带参数表或带地址操作符的函数名产生的指针。定义也可以是声明。如果该编译器还没有看到过名字A,程序员定义int A,则编译器马上为这个名字分配存储地址。声明常常使用于e x t e r n关键字。如果我们只是声明变量而不是定义它,则要求使用e x t e r n。对于函数声明,e x t e r n是可选的,不带函数体的函数名连同参数表或返回值,自动地作为一个声明。
C+ +要求必须写出函数原型(的全部信息),因为它增加了一个重要的安全层。
第四篇:C++编程知识总结
1.数组
1.1数组定义时的注意点
1在C++中不提供可变化大小的数组,○即数组定义中的常量表达式不能包含变量。(来源:C++书6.1.1)
int n;cin>>n;float t[n];上例在定义数组t时,变量n没有确定的值,即在程序执行之前,无法知道数组t的元素个数,所以这种声明不被允许。但是可以用new动态分配,如: int n;cin>>n;float *t;t=new float[n];
2在定义数组时,可以不直接指定数组的大小,由C++编译器根据初值表中元素的个数来自○动确定数组元素的个数。例如: int z[]={0,1,2,3,4,5,6,7,8} 3C++语言规定只能对数组中的元素进行赋值或引用,不能把整个数组作为一个整体进行赋○值或引用。(2.3是一个实例)(来源:C++书4同类型的数组之间不能相互赋值 ○如int a[5],b[5];a=b;//错误
strcpy(b,a);//正确
6.1.1)
1.2数组和指针的关系(来源:C++书8.2节8.2.1)
char s[5];在C++中说明了一个数组后,数组名可以作为一个指针来使用,因此s可作为一个指针使用(但它不同于指针,不能赋值运算、算术运算等)。
2.字符数组
2.1输入字符数据 char c;cin>>c;// cin不能将输入的空格赋给字符型变量。
cin.get();//可获得键盘上输入的每一个字符,包括空格和回车键。
2.2字符数组的输入/输出(来源:C++书6.2.4)2.2.1逐个字符输入 char c[10];for(int i=0;i<10;i++)cin>>c[i];2.2.2字符串输入 方法1 char c[10];cin>>c;//即在输入输出时只给数组名
此法在输入字符串时,遇到空格和回车就认为一个字符结束。方法2 cin.getline(字符数组名,允许输入的最大字符个数)此法可把输入的一行作为一个字符串送到字符数组中。
2.3字符数组和字符指针的初始化 2.3.1字符数组初始化 char tx[5]=“";2.3.2字符指针初始化 char *ptx=new char[5];ptx[0]=' ';2.4字符串赋值
方法1 char tx[4]=”abcd“;方法2 char tx[4];//tx=”abcd“;//错误,tx是数组名,不分配内存空间,不可以进行赋值操作;但是数组名可当指针使用(C++书8.2.1)。strcpy(tx,”abcd“);以上两种方法是数组
方法3是指向数组的指针方法 方法3 char *tx;tx=new char[4];tx=”abcd“ 方法4 char *tx=”abcde“;//这相当于根据数组元素的个数,确定数组的大小。tx指针指向这个数组。
//下面实例告诉我们,不仅字符数组与字符指针有区别,用new给定内存空间大小的字符指针与没给定内存空间大小的字符指针也是有区别的 voidmain(){
/*char s[6]=”“;
strcpy(s,”abcd“);cout<
/*char *s=new char[5];strcpy(s,”abcd“);
} cout<
char *s=”“;//分配了内存空间,但不知道大小 strcpy(s,”abcd“);//错误,使用时要注意!!cout<
3.指针
3.1指针可执行的运算
指针可以进行赋值运算、算术运算、关系运算。
1可以将与指针变量同类型的任一变量的地址赋给指针○2在C++中,可以(1)赋值运算:○
3同类型的指针变量之间可以将0赋给任一指针变量,其含义是初始化指针变量,使其为空○相互赋值,不同类型的经强制转换后也可以,通常也没意义。(2)算术运算:指针变量执行“++”或“——”,其含义是使指针变量指向下一个或上一个元素
3.2指针和数组(同1.2)3.3指向数组的指针变量
char(*p)[10];(*p)指明p是一个指针变量,再与[10]结合,表示该指针变量所指向的数据是一个一维数组,该数组由10个元素组成。3.4指针数组
由若干个同类型的指针所组成的数组称为指针数组,数组的每个元素都是一个指针变量。定义指针数组的格式:如char *p[10];由于“[]”的优先级比“*”高,p[10]构成一个数组,再与“*”结合,指明是一个指针数组。3.5指向指针的指针变量 char **pp;3.6 new运算符
注意点:
用new运算符分配的内存空间的指针值必须保存起来,以便于delete运算符归还已动态分配的内存,否则会出现不可预测的错误。3.6.1指向数组的指针 char* m_p1;m_p1=new char[10];//指针m_p1指向含有10个元素的数组空间。for(i=0;i<10;i++)m_p2[i]表示这10个数组元素。
voidmain(){ char *p1;//char b;p1=newchar[5];
//p1=”abcde“;//直接给p1赋字符串,下面for循环中是给每个元素赋值。for(inti=0;i<5;i++){ //b='c';
} p1[i]='a';//p1[i]是数组元素,不是指针
cout<
3.6.2指向指针的指针变量 char**m_p2;//指向指针的指针
m_p2=new char*[10];//指针m_p2指向含有10个元素的指针数组。for(i=0;i<10;i++)m_p2[i]表示这10个指针。
void main(){ char *p1;char **pp;p1=new char[5];pp=new char*[5];for(int i=0;i<5;i++){
p1[i]='a';=&p1[i];//pp[i]是指针
cout<
cout<
3.7 delete运算符
delete释放的不是指针本身,而是指针所指的对象。
4.容器类std::string #include
int main(int argc, char * argv[]){
std::string str=”abc“;
std::string::iterator cit=str.begin();
for(;cit!=NULL;++cit)//NULL比较,我估计肯定不对,虽然你说是可以通过编译
{
std::cout<<*cit< } return 0;} 4.容器类Vector 4.1迭代器和指针的区别 有时需要使用指向vector的一个指针,我们可以这样来做。 vector 表达式v[0]生产一个指向vector中第一个元素的引用,所以,&v[0]是指向那个首元素的指针。vector中的元素被C++标准限定为存储在连续内存中,就像是一个数组。 如果你在一个不好的环境中,他们会告诉你说可以用v.begin()代替&v[0],因为(这些讨厌的家伙将会告诉你)begin返回指向vector内部的迭代器,而对于vector,其迭代器实际上是指针。那经常是正确的,但正如条款50所说,并不总是如此,你不该依赖于此。 begin的返回类型是iterator,而不是一个指针,当你需要一个指向vector内部数据的指针时绝不该使用begin。如果你基于某些原因决定键入v.begin(),就应该键入&*v.begin(),因为这将会产生和&v[0]相同的指针。 这表明迭代器的内容*v.begin()才是vector中第一个元素。 4.2 容器vector的函数clear() 清空vector里所有的元素。因此,如AMProcessList析构函数里一个个删除vector中所有的元素是多此一举。 5.关键字operator 它是说明符,用于重载运算符。 6.函数可以将一个处理的结果值通过函数的Return语句返回,也可以通过参数将处理的多个结果带给调用者。 C++语言在处理函数调用时,参数是自右向左依次入栈的 7.类的前置声明 8.Const char* Bjarne在他的The C++ Programming Language里面给出过一个助记的方法: 把一个声明从右向左读。 char * constcp;(* 读成 pointer to)cp is a const pointer to char--->cp是一个指向字符char的固定指针 const char * ptr;ptr is a pointer to const char;--->ptr是一个指向固定字符char的指针 char const * p;--->无此形式 也就是说,cp和ptr都是指针,cp的值是不可改变的cp指向的内容是可变的;而ptr的值是可以改变的,ptr指向的内容是不可变的 9.String转化为Constchar*,Const char*转化为char* 1.在string里面string.c_str()函数把string转换为了const char*.○代码如下: stringa=”abcd“;const char*p=a.c_str();2const_cast 10.初始化 char *p;char *s=”“;char *t=NULL;p没分配内存,s分配了内存,t为空,11.变量的初始化 1指针需要初始化; ○2基本数据类型声明的变量需要初始化;如double m_dvalue;m_dvalue=0; ○3类声明的对象不需要初始化。○ 12.派生类中的一般成员函数和虚函数 classA { public: voidsolid(){cout<<”基类实函数“<<'n';} virtualvoidvir(){cout<<”基类虚函数“<<'n';} };classAA:publicA { public: voidsolid(){cout<<”派生类实函数“<<'n';} virtualvoidvir(){cout<<”派生类虚函数“<<'n';} }; voidmain(){ A* a=newA;AA* aa=newAA;a=aa; a->vir();//vir()是虚函数。它是运行时的多态性,即在程序运行时,根据具体的执行情况来动态的确定。因此输出”派生类虚函数“,而不是“基类虚函数” a->solid();//solid()是一般成员函数。它是编译时的多态性,即程序编译时就觉得会调用哪个函数。因为a是A类对象的指针,即使派生类AA对象的指针aa赋给a,在编译是已经觉得调用基类A的solid函数,因此输出“基类虚函数”而不是“派生类虚函数” aa->vir();aa->solid(); aa->A::solid();aa->A::vir();} Button newBtn = new Button();newBtn.Location = new System.Drawing.Point(128, 110);newBtn.Name = ”newBtn“;newBtn.Size = new System.Drawing.Size(75, 23);newBtn.Text = ”button2";newBtn.UseVisualStyleBackColor = true;this.Controls.Add(newBtn); ///清除新生成的Btn this.Controls.Remove(newBtn); CButton* CTextEditorView::NewMyButton(int nID,CRect rect,int nStyle){ CString m_Caption;m_Caption.LoadString(nID);//取按钮标题 CButton *p_Button = new CButton();ASSERT_VALID(p_Button);p_Button->Create(m_Caption, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | nStyle, rect, this, nID);//创建按钮 return p_Button;} 1.下列程序在32位linux或unix中的结果是什么? func(char *str) { printf(“%d”,sizeof(str)); printf(“%d”,strlen(str)); } main() { char a[]=“123456789”; printf(“%d”,sizeof(a)); func(a); } 答: 1049 网络/网络编程部份: 1、connect方法会阻塞,请问有什么方法可以避免其长时间阻塞? 答:最通 常的方法最有效的是加定时器;也可以采用非阻塞模式。 2、网络中,如果客户端突然掉线或者重启,服务器端怎么样才能立刻 知道? 答:若客户端掉线或者重新启动,服务器端会收到复位信号,每一种tcp/ip得实现不一样,控制机制也不一样。 3.在子网210.27.48.21/30种有多少个可用地址?分别是什么? 答: 简: 30表示的是网络号(network number)是30位,剩下2位中11是广播(broadcast)地址,00是multicast地址,只有01和10可以作为host address。 详: 210.27.48.21/30 代表的子网的网络号是30位,即网络号是210.27.48.21 & 255.255.255.251=210.27.48.20,此子网的地址空间是2位,即可以有4个地址:210.27.48.20, 210.27.48.21, 210.27.48.22, 210.27.48.23。第一个地址的主机号(host number/id)是0,而主机号0代表的是multicast地址。最后一个地址的最后两位是11,主机号每一位都为1代表的是广播(broadcast)地址。所以只有中间两个地址可以给host使用。其实那个问题本身不准确,广播或multicast地止也是可以使用的地址,所以 回答4也应该正确,当然问的人也可能是想要你回答 2。我个人觉得最好的回答是一个广播地址,一个multicast地址,2个unicast地址。 4.TTL 是什么?有什么用处,通常那些工具会用到它?(ping? traceroute? ifconfig? netstat?) 答: 简:TTL 是Time To Live,一般是hup count,每经过一个路由就会被减去一,如果它变成0,包会被丢掉。它的主要目的是防止包在有回路的网络上死转,浪费网络资源。ping和 traceroute用到它。 详: TTL是Time To Live,目前是hup count,当包每经过一个路由器它就会被减去一,如果它变成0,路由器就会把包丢掉。IP网络往往带有环(loop),比如子网A和子网B有两个路由器 相连,它就是一个loop。TTL的主要目的是防止包在有回路的网络上死转,因为包的TTL最终后变成0而使得此包从网上消失(此时往往路由器会送一个 ICMP包回来,traceroute就是根据这个做的)。ping会送包出去,所以里面有它,但是ping不一定非要不可它。traceroute则是 完全因为有它才能成的。ifconfig是用来配置网卡的,netstat-rn 是用来列路由表的,所以都用不着它 5.路由表示做什么用的?在linux环境中怎么来配置一条默认路由? 答: 简:路由表是用来决定如何将包从一个子网传送到 另一个子网的,换局话说就是用来决定从一个网卡接收到的包应该送的哪一张网卡上的。在Linux上可以用“route add default gw <默认路由器IP>”来配置一条默认路由。 详: 路由表是用来决定如何将包从一个子网传送到另一个子网的,换局话说就是用来决定从一个网卡接收到的包应该送的哪一张网卡上的。路由表的每一行至少有目标网 络号、netmask、到这个子网应该使用的网卡。当路由器从一个网卡接收到一个包时,它扫描路由表的每一行,用里面的netmask和包里的目标IP地 址做并逻辑运算(&)找出目标网络号,如果此网络号和这一行里的网络号相同就将这条路由保留下来做为备用路由,如果已经有备用路由了就在这两条路 由里将网络号最长的留下来,另一条丢掉,如此接着扫描下一行直到结束。如果扫描结束任没有找到任何路由,就用默认路由。确定路由后,直接将包送到对应的网 卡上去。在具体的实现中,路由表可能包含更多的信息为选路由算法的细节所用。题外话:路由算法其实效率很差,而且不scalable,解决办法是使用IP 交换机,比如MPLS。 在Linux上可以用“route add default gw <默认路由器IP>”来配置一条默认路由。 6.在网络中有两台主机A和B,并通过路由器和其他交换设备连接 起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排除故障? 答:测试这两台 机器是否连通:从一台机器ping另一台机器 如果ping不通,用traceroute可以确定是哪个路由器不能连通,然后再找问题是在交换设备/hup/cable等。 7.网络编程中设计并发服务器,使用多进程 与 多线程,请问有什么区别? 答案一: 1,进程:子进程是父进程的复制 品。子进程获得父进程数据空间、堆和栈的复制品。 2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数 据,但拥有自己的栈空间,拥有独立的执行序列。 两者都可以提高程序的并发度,提高程序运行效率和响应时间。 线程和进程在使用上各有优缺 点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。 答案二: 根 本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的: 1。速度: 线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。 2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空 间内。 3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。第五篇:linux c网络网络编程面试题收集