C++学习(30)

来源:互联网 发布:java 北京尚学堂视频 编辑:程序博客网 时间:2024/06/06 03:43

1.全局变量如果加上static关键字,就相当于各自文件的本地全局变量。如果不加static,链接时会出错。

全局变量可以定义在可被多个.c文件包含的头文件中。

 

2.下列for循环的循环体执行次数为:0

for(int i=10,j=1;i=j=0;i++,j--)

分析:i=j=0;是一个赋值语句,位于for的判断位置,就只当成条件真假,C中0为假,非0为真,

所以i=j=0,结果就是i=0,为假,则循环不进入;

如果是 i=j=1,结果就是真,可以进入循环,并且无限。

 

3.下列程序输出结果:

#include<iostream>#include<string.h>using namespace std;int main() {   chara[10]={'1','2','3','4','5','6','7','8','9',0},*p;int i;   i=8;   p=a+i;   printf("%s\n",p-3);}

分析:1)、p指向a[5]
2)、“%s”输出直到'\0'的字符串
3)、最后的0为数字而非字符‘0’,ASIIC码中0为空字符
所以输出的结果是6789而不是67890

 

4. 在《C++primer》说过如果子类没有显示地调用父类的构造方法,则默认地调用父类的不带参数的构造方法,这里父类定义了带参数的构造方法,则默认构造函数失效了,需要显式地调用父类的构造方法。

 

当基类构造函数需要外部传递参数才能进行初始化时,派生类必须显式定义构造函数,为基类传递参数;基类如果不需要传递或者可以不传递参数,派生类可以不用显式定义构造函数。

 

5.

#include<iostream>#include<string.h>usingnamespace std;intmain() {   int a[3];   a[0]=0,a[1]=1,a[2]=2;   int *p,*q;   p=a;   q=&a[2];   cout<<p<<""<<q<<endl;   cout<<q-p<<endl;}

分析:可以先想想指针指示数组时,移动的情况, 类比得到指针加减其实是加减的类型的字节的大小。

所以:指针 ptr的类型是 int*, 它指向的类型是 int ,它被初始化为指向整形变量 a 。接下来的第 3 句中,指针 ptr 被加了 1 ,编译器是这样处理的:它把指针 ptr 的值加上了 sizeof(int) ,在 32 位程序中,是被加上了 4。

单独打印p和q都会显示地址,但是加减运算就会变成以指针类型(这里是整型)为基本单位的加减运算。1个整型就是1。

 

6.在基类和派生类中,派生类可以定义其基类中不具备的数据和操作。对两个有相同名字的数据成员进行访问时,如果没有作用域分隔符限定时,对此数据成员的访问将出现歧义。

 

7.有如下程序:

#include<iostream>#include<string.h>using namespace std;class A{public:   int _a;   A(){     _a=1;   }   void print() {     cout<<_a;   }}; class B:public A {   public:     int _a;     B() {        _a=2;     }};int main() {   B b;   b.print();   cout<<b._a;}

分析:因为在继承的时候,允许子类存在与父类同名的成员变量,但是子类屏蔽父类的同名成员变量,他们同时存在。 因为给孩子类中没有定义print函数,所以会按照就近原则去寻找父类中是否有print函数。恰好父类中有这个函数,于是调用父类的print函数b.print(),而这个函数会调用父类的a变量。

 

8.

#include<iostream>#include<string.h>using namespace std;int main() {   int b;   char c[10];   scanf("%d%s",&b,c);//scanf("%d%s",&b,&c);都是一样的。}


9.在64位系统下,根据如下程序,输出结果为:80 8

#include<iostream>#include<string.h>using namespace std;int main() {   char *p[10];   char (*p1)[10];   cout<<sizeof(p)<<""<<sizeof(p1);}

分析:重点理解p跟谁结合了,跟[]结合,则p就是一个数组;跟*结合,p就是一个指针;
首先[]()的优先级一样,均大于*
char *p[10],p与[]结合,所以p就是一个数组,数组的元素比较特殊,是指针,指针大小为8,所以是10*8=80;
char(*p1)[10],与*结合,所以是一个指针,大小为8

 

分析二:

char *p[10]是指针数组,数组里存放了10个指针,在64位系统下指针占8个字节,所以sizeof(p)=10*8=80.
char(*p1)[10]是数组指针,p1是一个指向存放10个char类型的数组的指针,所以sizeof(p1)=8.

 

10.JAVA没有指针的概念,被封装起来了,而C++有;

JAVA不支持类的多继承,但支持接口多继承;C++支持类的多继承;

C++支持操作符重载,JAVA不支持;

JAVA的内存管理比C++方便,而且错误处理也比较好;

C++的速度比JAVA快。
C++更适用于有运行效率要求的情况;JAVA适用于效率要求不高,但维护性要好的情况。

 

11.在linux+gcc下,关于以下代码正确:ABD

#include<iostream>#include<string.h>using namespace std;std::string& test_str() {   std::stringstr="test";   return str;}int main() {   std::string&str_ref=test_str();   std::cout<<str_ref<<std::endl;   return 0;}

A编译警告    B返回局部变量引用,运行时出现未知错误

C正常编译且运行  D把代码中的&号都去掉,程序正常运行


分析:引用返回的是局部变量本身,而不是复制一份再返回,所以结果难以预料;

其次返回局部自动变量是可以的,只要不是地址或引用就可以,否则需要将变量声明成static类型。

c++中string是一个类,去掉&后return时会调用复制构造函数,因此是可以得到所要的内容。

 

12.

#include<iostream>#include<string.h>using namespace std;int main() {   inta[10]={0,1,2,3,4,5,6,7,8,9},*p=a;   cout<<*p<<endl;   cout<<p[6]<<""<<*(a+6)<<" "<<*(p+6)<<""<<p+6<<endl;   return 0;}

分析:p[6]、*(a+6)、*(p+6)是一个意思;  a+6与p+6是一个意思,都是取地址。

 

13.使用char *p=new char [100]申请一段内存,然后使用delete p释放,会有声明问题?

不会有内存泄露,但不建议用。

分析:C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用new[]分配的一组对象的内存空间的时候用delete[]。

关于new[]和delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。

基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用delete和delete[]都是应该可以的;但是对于类对象数组,只能用delete[]。

所以一个简单的使用原则就是:new和delete、new[]和 delete[]对应使用。

点击打开链接

 

14.面向对象的五大基本原则:

单一职责、开放封闭、里氏替换、依赖倒置、接口隔离


五个基本原则:
单一职责原则(Single-ResposibilityPrinciple):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。
开放封闭原则(Open-Closed principle):软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。
Liskov替换原则(Liskov-Substituion Principle):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。
依赖倒置原则(Dependecy-InversionPrinciple):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。
接口隔离原则(Interface-SegregationPrinciple):使用多个小的专门的接口,而不要使用一个大的总接口

 

15.

#include<iostream>#include<string.h>using namespace std;struct st_t{   int status;   short *pdata;   char errstr[32];};int main() {   st_t st[16];   char *p=(char*)(st[2].errstr+32); //第3个的errstr字符串+32后是第4个结构体的首指针   cout<<sizeof(st_t)<<endl; //sizeof(st_t)是40   printf("%d",(p-(char*)(st))); //p减去第0个指针就求4-0中间有多少字节,就是40*3=120


16.

 auto fn=[](unsigned char a) {     cout<<std::hex<<(int)a<<endl; }; fn(-1);

分析:-1=1000 0001
存储的是补码形式
①反码是1111 1110【符号位不变,其他位取反】
②补码是1111 1111【反码加1】
然后std::hex以十六进制显示【1111 1111】

 

17.抽象类的指针可以指向不同的派生类。

用关键字virtual修饰的成员函数叫做虚函数虚函数是为了实现多态而存在的,必须有函数体;
纯虚函数的声明,是在虚函数声明的结尾加0没有函数体。在派生类中没有重新定义虚函数之前是不能调用的;
如果一个类中至少含有一个纯虚函数,此时称之为抽象类。所以抽象类一定有纯虚函数;
基类类型的指针可以指向任何基类对象或派生类对象

 

原创粉丝点击