C++学习(43)

来源:互联网 发布:2016中国经济数据 编辑:程序博客网 时间:2024/05/29 04:48

1.  分析下列程序:出错。

#include<iostream>#include<cstdlib>#include<string.h>using namespace std;int main() {  char*p1="123",*p2="ABC",str[50]="xyz";  strcpy(str+2,strcat(p1,p2));  printf("%s\n",str);  return 0;}

分析:原代码有错:p1和p2都指向常量字符串,在常量区,所以不能对其进行操作;改为数组即可,但是用字符串初始化数组时要记得将数组长度加1,因为字符串默认的末尾有一个‘\0’;第二点要注意的是,strcat函数的p1要有足够的空间来容纳p1和p2连接后的串长。

 

常量指针p1和p2,我们可以改变它指向的地址,但不能改变指向的内容,如果改变就出错。

 

2.  分析下列程序:输出正常结果。

#include<iostream>#include<cstdlib>#include<string.h>using namespace std;int main(int argc,char **argv) {  cout<<"welcometo sogou"<<endl;  return 0;}

分析:main函数可以接受两个参数int main(intargc,char *argv[]),argc=arguments count表示参数个数,argv=argumentvector表示数组指针,同时数组在参数传递时会转义为指针,即使[]中包含维度也会被忽略!所以char *argv[]等价于char **argv。

“welcome to sogou"是以‘\0'结尾的字符常量数组,可以直接输出。


3.  分析下列代码共调用多少次拷贝构造函数:7

Widget f(Widget u){  Widegt v(u);  Widget w=v;  return w;}main() {  Widget x;  Widget y=f(f(x));}

分析:拷贝构造函数主要在以下三种情况下起初始化作用:

1).在声明语句中用一个对象初始化另一个对象;

2).将一个对象作为参数按值调用方式传递给另一个对象时生成对象副本;

3).生成一个临时对象作为函数的返回结果。

调用顺序

Widget u;  

Widget v(u);

Widget w=v;

return w;

Widget v(u);

Widget w=v;

return w;

 

4.

D: p = &x;表示的是,对x取地址,赋值给指针p,那么p 将指向 x 的那块内存空间,但是 x  是形式参数(也有人说是方法参数,都可以),函数调用完了之后,内存就释放了,所以再返回 *p(即取出那块内存空间的值),已经找不到了。所以错误。

 

A:*p = x; 表示的是将 x 的值赋值给 P 所指向的空间,而p之前并没有指向任何地方,这个操作将是非法的。

 

C:*p = new int(x);这个操作同 A 的结果一样。

 

B: p = new int(x);newint(x) 新申请空间,调用完后不释放空间,所以将地址赋值给p 则p 指向了这段新申请内存空间,所以当做 *p 返回时,即取出p所执行空间的值,所以会输出5.

 

5.分析一:

//FUNC1和FUNC2都是函数类型 typedefint(FUNC1)(intin); typedefint(FUNC2)(int*,int*,int*);

//如果此处不修改,需要修改代码第14行,修改后代码如下

FUNC1 *p = &inc;

//或者

FUNC1 *p = inc;

这两种初始化的方式都是合法的。

 

//如果修改代码第10行和第11行,代码如下: //FUNC1和FUNC2都是指向函数的指针  typedef int (*FUNC1)(intin);

typedef int (*FUNC2)(int*,int*,int*);代码第14行不需要做任何修改。

 

分析二:typedefint(FUNC1)(intin);是函数指针定义;

show(multi,10,&a);FUNC2类型函数指针fun指向函数multi的首地址;
FUNC1 p=&inc;FUNC1类型函数指针p指向函数inc的首地址;

int temp=p(arg1);此时调用函数inc,参数为10,返回值为11;
fun(&temp,&arg1,arg2);调用函数multi,参数为(11,10,arg2),arg2为指针变量负责带回返回值;
printf("%d\n",*arg2);输出110;

 

6.(1)所谓虚函数就是多态情况下只执行一个,而从继承的概念来讲,总是要先构造父类对象,然后才能是子类对象,如果构造函数设为虚函数,那么当你在构造函数时就不得不显示的调用构造,还有一个原因就是为了防错,试想如果你在子类中一不小心重写了个跟父类构造函数一样的函数,那么你的父类的构造函数将被覆盖,也即不能完成父类的构造.就会出错.因此不要将构造函数声明为虚函数。 

(2)在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。

(3)在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。

 

A:构造函数不能声明为虚函数的原因是:

1) 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类,无法确定。

 

2虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让它指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初始化,将无法进行。

 

7.友元关系是单向的,不能被继承,不是对称,不能传递。

 

8.C++中的继承方式有:

public、private、protected三种(它们直接影响到派生类的成员、及其对象对基类成员访问的规则)。

(1)public(公有继承):继承时保持基类中各成员属性不变,并且基类中private成员被隐藏派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象只能访问基类中的public成员。

(2)private(私有继承):继承时基类中各成员属性均变为private,并且基类中private成员被隐藏派生类的成员也只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。

(3)protected(保护性继承):继承时基类中各成员属性均变为protected,并且基类中private成员被隐藏派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。

 

9.两个线程并发执行以下代码,假设a是全局变量,那么以下输出哪个是可能?

int a=1;

Void foo() {

  ++a;

  printf(“%d”,a);

}

A 3 2B 2 3 C3 3 D 2 2

分析:假设线程x和y同时执行,x和y可随时被抢占,a的初始值为1

 

A:3, 2

y先执行++a,a为2;

y再执行printf,a入栈,在打印到终端之前切换到x

x执行++a,a为3;

x执行printf,输出3;再切换到y

y执行打印,输出2

 

B:2 3

x先执行++a,a为2;

x再执行printf,输出2;切换到y

y执行++a,a为3;

y执行printf,输出3;

 

C:3 3

x先执行++a,a为2;切换到y

y执行++a,a为3;

y执行printf,输出3;切换到x

x执行printf,输出3

 

D:2 2

类似C, 执行++a操作但没有写回到内存

这里关键有两点:

(1)两个线程可随时被抢占

(2)++a和printf不是原子指令,可随时被打断;特别注意函数printf,a作为参数压栈后,a再变化则不会影响输出(printf实际打印的是压栈的参数,是值拷贝的栈变量)

 

10.在C++语言中,为了结束由while语句构成的循环,while后一对圆括号中表达式的值应该为:0.

原创粉丝点击