C++学习(29)

来源:互联网 发布:巨人网络股票代码 编辑:程序博客网 时间:2024/06/08 12:13

1.   C++中如果没有声明权值,class关键字定义的类,其成员默认的访问属性为private;

Java中如果没有声明权值,则是默认的包访问权限;

 

2.友元函数不含this指针,所以不可以通过对象名访问对象。

 

3.假定指针变量p定义为“int *p=new int(100);”要释放p所指向的动态内存,应该使用:delete p;

分析:注意()是初始化,[]是定义多个对象。

int* p = new int (100)是创建一个int型的内存,并赋值为100;

int *p = new int[100]是创建100个int型的内存;

一般用法是new一个数组的话一般是delete[],其他的直接delete即可。

但是其实对于内置数据类型,其实是delete[]和delete都可以的。

 

delete做了两件事:

调用一次 p指向的对象的析构函数; 调用operator delete(p);释放内存。 因为int为内置类型所以不存在第二件事,若是delete一个有析构函数的类类型就会造成内存泄露 用delete释放一个动态数组的条件是对象的类型是内置类型或者是无自定义的析构函数的类类型。

 

4.C++函数可以嵌套调用,但是不可以嵌套定义。

 

5.1) malloc 函数: void *malloc(unsigned int size)

在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。

2)calloc函数: void *calloc(unsignedint num, unsigned int size)

按照所给的数据个数和数据类型所占字节数,分配一个 num* size连续的空间。calloc申请内存空间后,会自动初始化内存空间为0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。

3)realloc函数: void *realloc(void*ptr, unsigned int size) 动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。申请的内存空间不会进行初始化。

4new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在函数体外定义的变量都初始化为0,在函数体内定义的内置类型变量都不进行初始化。

 

5.下述代码最后会产生多少个进程?点击打开链接

int main() {   int I;   for(i=0;i<5;i++){     intpid=fork();     if(pid==0){     }     else{     }   }return 0;}

分析:i=0时,共有两个进程: 主进程和主进程创建的第一个进程
i=1时,以上两个进程分别创建新的进程,此时共有四个进程
i=2时,以上四个进程分别创建新的进程,此时共有8个进程
....
依次类推, 当i=n时,共创建2^(n+1)个进程

 

分析二:先不考虑主线程,i=4时,由fork()创建出一个线程;

i=3时,由fork()创建出一个线程,主线程和新线程中i变为4,所以共创建了1+2*1=3个;

i=2, 由fork()创建出一个线程,主线程和新线程中i变为3,所以共创建了1+2*3=7个;

i=1, 由fork()创建出一个线程,主线程和新线程中i变为2, 所 以共创建了1+2*7=15个;

i=0, 由fork()创建出一个线程,主线程和新线程中i变为1, 所 以共创建了1+2*15=31个;

加上主线程,共32个;

 

6.分析下述代码运行结果:

#include<iostream>#include<string.h>using namespace std;int main() {   chararr[]={4,3,9,9,2,0,1,5};   char*str=arr;   cout<<sizeof(arr)<<""<<sizeof(str)<<""<<strlen(str)<<<" "sizeof(*str)<endl;}

7.下列代码运行结果:不确定。

#include<iostream>#include<string.h>using namespace std;char *getMem(void) {     charp[]="hello world";     p[5]=0x0;     returnp;}void test(void) {     char*s=0x0;     s=getMem();     printf(s);}int main() {   test();}

分析:应该注意char p[]="helloworld";和char *p="hello world"的区别;前者存放在栈里,后者存放在静态区

不能返回栈变量的或者栈变量的引用。这里getMem(void)中返回的是一个栈中的数组名也就是数组的地址,当返回这个地址后这个地址所给的空间就被释放了,也就是这块空间可以被其他进程使用,所以这块空间的数据可能还是原来的数据也可能不是原来的数据,下来在test函数中s是一个字符指针,此时printf就通过s所接受的getMem(void)函数返回的地址开始读取字符(因为s是char*,所以读的是字符),直到找到一个'\0'也就是数字0的时候才结束输出字符

 

8.分析下述代码结果: 2 1

#include<iostream>#include<string.h>using namespace std;int main() {   inta,x;   for(a=0,x=0;a<=1&&!x++;a++) {     a++;   }   cout<<a<<x<<endl;}

9.分析下述代码结果:

#include<iostream>#include<string.h>using namespace std;class A{   public:     A(){        cout<<"0";     }     A(inta) {        cout<<"1";     }     A&operator=(const A& a) {        cout<<"2";        return*this;     }};int main() {   A a1;   a1=10;}

分析:A a1;//调用A默认构造函数
a1=10;//类型不匹配,调用构造函数A(int)进行隐式转化,之后将引用传给operator=()

 

10.一个类成为抽象类,至少必须有一个纯虚函数。包含一个纯虚函数的类视为抽象类。

 

11.在32位cpu上选择缺省对齐情况下,如下的结构体定义:

struct A{   unsigneda:19;   unsignedb:11;   unsignedc:4;   unsignedd:29;   char index;}

则sizeof(struct A)的值为:16

分析:由于在32位cpu上选择缺省对齐的情况下,所以每行支持4个字节即32bit;于是a和b公用一行19+11 = 30bit还剩2bit,又因为c为4bit,无法全部占据该行,故其重新开始一行,第一行空出2bit;对于第二行,c占据前4bit,同理由于d占据29bit, 无法全部占据该行的剩余空间,所以其处于第三行,第二行中剩余28bit空出;同理可分析第三行和第四行的空间,由于第四行占据1个直接,为了保持对其需要把剩余空间补齐;

综上该结构体的大小为4*4个字节 = 16

 

12.如果有一个类是myClass,关于下面代码:

myClass ::~myClass() {

delete this;

this=NULL;

}

无法编译通过。(因为this指针为const,不允许进行修改)

如果~myClass() {delete this;}代码如此,则能通过编译,不会导致栈溢出,因为不断调用析构函数。

 

13.虚函数是可以内联的

据我所知有3种情况会内联展开:

1.“虚函数静态调用,像这样
pclass->class::vf()
或者在类体内调用class::vf()//相当于this->class::vf()
这种调用方式其实类似第二种调用方式。
如果你可以肯定你要调用的函数,并且这个函数不需要运行时刻确定的数据。就可以这样写来提高效率。不错吧:)

2.用一个类对象通过成员选择符.调用虚函数,如
obj.vf()
这时虚函数vf()就可以被vc优化内联展开。
这样调用等于告诉编译器你要调用的具体函数,在函数有inline修饰或是体内定义的情况下就会被内联展开。

3.虚析构函数的静态调用链一定会被内联展开(Lippman文章提到的,见后附文章)

不会内联的情况:
用类指针调用虚函数如pclass->vf()
这时vf()无论如何不会被内联展开。(即便使用__forceinline关键字强制内联也不行,最终他还是一个非inline函数。)
通过类指针调用虚函数,由于虚函数的多态性,那就意味着你在告诉编译器,这是一个需要在运行时动态调用的函数,不要把它内联。

点击打开链接

 

14.delete子类对象是一定会调用父类的析构函数的。先调用子类的析构函数然后调用父类的析构函数;如果要调用父类指向子类的对象,此时才需要父类的析构函数是虚的。

A* a = new B //B是A的子类

delete a

此时父类析构函数需要加virtual,如果不加virtual,那么最后只会调用A的析构函数,而派生的部分,并没有得到析构,导致内存泄露。

 

B* b = new B

delete b

此时父类析构函数并不需要加virtual,就是一个很正常的构造析构过程

A() -> B() -> ~B() -> ~A()

 

15.声明类的运算符成员函数时可以省略一个参数。例如,一个参数时可以不用形参,参数只用一个形参,因为成员函数有this指针表示自己。但是若是类的友元函数声明运算符时,不能省略,因为这种情况下this指针并不是该类的类型。

 

类中重载运算符的一般格式是:

类名operator运算符(参数) 如Data operator + (Data);

原创粉丝点击