C++小题(二)

/*
void func()
{
  static int val;
  …
}
*/
/*
静态局部变量有以下特点:
该变量在全局数据区分配内存;
静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
*/
/*
int func(int x){
    int count=0;
    while (x)
    {
        count++;
        x=x&(x-1);//与运算
    }
    return count;
}
//8
*/
/*
#include<iostream>
using namespace std;
class X{
public:
	virtual ~X(){}
};
class A{
	public:
	virtual ~A(){}
};
class B:public A{
	public:
	virtual ~B(){}
};
class C:public B{
	public:
	virtual ~C(){}
};
class D:public X,public C{
	public:
	virtual ~D(){}
};
int main(){
	A *pa=new D;
	X* px=dynamic_cast<X*>(pa);
	cout<<px<<endl;
	
	D *pd=new D;
	A *pa2=dynamic_cast<A*>(pd);
	cout<<pa2<<endl;
	
	B *pb=new B;
	D *pd2=dynamic_cast<D*>(pb);//转换失败 
	cout<<pd2<<endl;
	
	A *pa3=new C;
	C *pc=dynamic_cast<C*>(pa);
	cout<<pc<<endl;
}

//dynamic_cast<>用于C++类继承多态间的转换,分为:
//1.子类向基类的向上转型(Up Cast)
//2.基类向子类的向下转型(Down Cast)
//其中向上转型不需要借助任何特殊的方法,只需用将子类的指针或引用赋给基类的指针或引用即可,dynamic_cast向上转型其总是肯定成功的。

//而向下转换时要特别注意:dynamic_cast操作符,将基类类型的指针或引用安全的转换为派生类的指针或引用。
//dynamic_cast将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。
//这也是dynamic_cast与其他转换不同的地方,dynamic_cast涉及运行时类别检查,如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast失败。
//如果是指针类型失败,则dynamic_cast的返回结果为0,如果是引用类型的失败,则抛出一个bad_cast错误。
//注意:dynamic_cast在将父类cast到子类时,父类必须要有虚函数。因为dynamic_cast运行时需要检查RTTI信息。只有带虚函数的类运行时才会检查RTTI。
*/

/*
//如果两段内存重叠,用memcpy函数可能会导致行为未定义。 而memmove函数能够避免这种问题,下面是一种实现方式,请补充代码。 
#include <iostream>
#include<cstring>
using namespace std;
void* memmove(void* str1,const void* str2,size_t n)
{
    char* pStr1= (char*) str1;
    const char* pStr2=(const char*)str2;
    if  (pStr1<pStr2 ) {
        for(size_t i=0;i!=n;++i){
            *(pStr1++)=*(pStr2++);
        }
    }
    else{
        pStr1+=n-1;
        pStr2+=n-1;
        for(size_t i=0;i!=n;++i){
            *(pStr1--)=*(pStr2--);
        }
    }
    return str1;
}
int main(){
	char* pstr1=new char('a');
	char* pstr2=new char('b');
	cout<<pstr1<<endl<<pstr2<<endl;
	cout<<(pstr1>pstr2?1:-1)<<endl;
	pstr2=(char*)"aaaaaaaaaaaaadddddddasdads";
	pstr1=(char*)memmove(pstr1,pstr2,strlen(pstr2)+1);
	cout<<pstr1;
}*/

/*
#include<stdio.h>
#include<string.h>
#include <pthread.h>
  
void* print1(void* data){
    printf("1 ");
}
  
void* print2(void* data){
    printf("2 ");
}
 
void* print3(void* data){
    printf("3 ");
}
 
int main(void){
    pthread_t t,t1,t2;
     
    pthread_create(&t,0,print1,NULL);
    pthread_create(&t1,0,print2,NULL);
    pthread_create(&t2,0,print3,NULL);
     
    pthread_join(t,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    printf("\n");
}
//结果不确定
//pthread_join(t,NULL)意思是等待线程t执行结束了再执行下面的代码。
//但在这之前,3个线程都已提交,它们可能都已经顺序随机地执行了,也可能没有,所以结果也是不可预测的。 
//pthread_create(&t, 0, print1, NULL);
//pthread_join(t, NULL);
//pthread_create(&t1, 0, print2, NULL);
//pthread_join(t1, NULL);
//pthread_create(&t2, 0, print3, NULL);
//pthread_join(t2, NULL);
//这样才是按顺序的。
*/



//定义一个函数指针,指向的函数有两个int形参并且返回一个函数指针,返回的指针指向一个有一个int形参且返回int的函数?
// int (*(*F)(int, int))(int)


/*
#include <iostream>
#include<cstring>
using namespace std;
int main(){
	int (**a1)[3][4];
	int (*a2)[3][4];
	int **a3[3][4];
	int *a4[3][4];
	cout<<sizeof(a1)<<" "<<sizeof(a2)<<" "<<sizeof(a3)<<" "<<sizeof(a4)<<endl;//4 4 48 48
	cout<<sizeof(a3[0])<<" "<<sizeof(a4[0])<<endl;//16 16
	cout<<sizeof((*a1)[0])<<" "<<sizeof((a2)[0])<<endl;//48 48
}
*/



//下面关于多态性的描述,错误的是:
//正确答案: C   你的答案: C (正确)

//A,C++语言的多态性分为编译时的多态性和运行时的多态性
//B,编译时的多态性可通过函数重载实现
//C,运行时的多态性可通过模板和虚函数实现
//D,实现运行时多态性的机制称为动态绑定

//A,正确,分编译时多态和运行时多态
//B,编译时多态可以通过函数重载实现,具体表现在根据参数的个数和类型不同选择合适的同名函数
//C,运行时多态通过虚函数实现,就是运行时根据对象类型自动选择正确的调用接口。模板属于编译时多态性,因为编译时自动根据模板生成模板函数。
//D,运行时多态是根据对象类型自动选择正确的调用函数,也叫动态绑定。

/* 
#include<iostream>
#include<string>
std::string& test_str()
{
   std::string str="test";
   return str;
}
int main()
{
	std::string& str_ref=test_str();
   std::cout<<str_ref<<std::endl;
   return 0;
}
//返回局部变量引用运行时出错, 去掉&后,又产生非const左值引用不能绑定右值 ,所以&要都去掉 
*/



/*
char* s="AAA"; //1
printf("%s",s); //2
s[0]='B'; //3
printf("%s",s); //4
//哪一句会出错?
//3出错,初始化指针时所创建的字符串常量被定义为只读。
//如果试图通过指针修改这个字符串的值,程序就会出现未定义的行为。S[0]只可读,不可写 
*/

/*
内联函数在编译时是否做参数类型检查?
宏和函数的区别: 
1. 宏做的是简单的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数的参数的传递,参数是有数据类型的,可以是各种各样的类型. 
2. 宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的. 
3. 宏在编译之前进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间. 
4. 宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的. 
5. 函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的.
内联函数与宏的区别:
1.内联函数在运行时可调试,而宏定义不可以;
2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会; 
3.内联函数可以访问类的成员变量,宏定义则不能; 
4.在类中声明同时定义的成员函数,自动转化为内联函数。
*/

/*
死代码主要是指1. 执行不到的代码. 
2. 执行得到, 但没有任何作用的代码。 
总而言之一句话: 死代码就是“不产生实际作用”的代码
比如while(false){}
*/

/*
有这样一个类:
class Eye
{
    public:
    void Look(void);
};
现在希望定义一个Head类,也想实现Look的功能,应该使用()方法,实现代码重用。
继承
组合
模板
过滤
组合是在新类中以原有类的对象作为数据成员,继承是在不改变现有的类的基础上,采用现有类的形式并在其中添加新代码,
组合一般用于在新类中使用现有类的功能而不是他的接口的情况,就是新类用户看到的只是为新类所定义的接口。
而继承则是用于在新类需要向基类转化的情况(多态),这也是组合和继承使用的最清晰的判断方法。
结合本题分析,我们只需让眼睛作为头的一个成员即可,而让头去继承眼睛的特性是没有必要的。
因此这道题选择组合 
*/

/*
32位编译器下,sizeof(void)的值是多少?
编译错误或者为1
*/ 

/*
由多个源文件组成的C程序,经过编辑、预处理、编译,链接等阶段会生成最终的可执行程序。下面哪个阶段可以发现被调用的函数未定义?   
预处理  编译 链接 执行
选链接阶段。 
A: 预处理是 C 语言程序从源代码变成可执行程序的第一步,主要是 C 语言编译器对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等。
B: 编译之前,C 语言编译器会进行词法分析、语法分析 (-fsyntax-only) ,接着会把源代码翻译成中间语言,即汇编语言 。 编译程序工作时,先分析,后综合,从而得到目标程序。所谓分析,是指词法分析和语法分析;所谓综合是指代码优化,存储分配和代码生成。 值得一提的是,大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,但也有的编译程序则先产生汇编语言一级的符号代码文件,然后再调用汇编程序进行翻译加工处理,最后产生可执行的机器语言目标文件。 
C: 链接是处理可重定位文件,把它们的各种符号引用和符号定义转换为可执行文件中的合适信息( 一般是虚拟内存地址 ) 的过程。
*/ 

/*
#include<iostream>
#include<cstring>
using namespace std;
int main(){
	char arr[] = {4, 3, 9, 9, 2, 0, 1, 5};
char *str = arr;
cout<<sizeof(arr);//8
cout<<sizeof(str);//4
cout<<strlen(str);//5
}
//strlen(str)求字符串长度,这里的字符串由字符数组得到,字符数组中赋值的是字符的ASCII值
//其中ASCII值为0的代表字符中的‘\0’也就是字符串结束的标志,所以得到的长度为5 
*/

/*
用变量a给出下面的定义:一个有10个指针的数组,该指针指向一个函数,该函数有一个整形参数并返回一个整型数
int (*a[10])(int);
遵循这样的规则:从右向左,由近及远,括号优先;比如 
从a符号开始。其右边是[10],说明a是个数组,其中存了十个元素。 
再看a的左边是一个*。说明数组中存的是指针。 
现在在看(*a[10])的右边是(int);说明所存的指针是指向有一个int形参的函数 
现在看(*a[10])的左边是个int ,说明指向的函数的返回值为int 类型
*/



/*
#include<iostream>
using namespace std;
int main(void) {
    http://www.taobao.com
    cout << "welcome to taobao" << endl; 
}
//中 http相当于一个label,//www.taobao.com是注释,所以答案错误,可以运行
*/




//编译器只需要能够完成指定字节序(从c代码到机器码)的翻译即可,由任意语言实现都可以。




/*
#include<iostream>
using namespace std;
int main(){
    int n[][3]={10,20,30,40,50,60};
    int (*p)[3];
    p=n;
    cout<<p[0][0]<<","<<*(p[0]+1)<<","<<(*p)[2]<<endl;//10 20 30
}

//一维数组:
//  a <=> &a[0]        a+1 <=> &a[1]
// *a <=> a[0]         *(a+1) <=> a[1]
//二维数组:
//     a[0] <=>&a[0][0]    a[1] <=> &a[1][0]      a[1]+1 <=> &a[1][1]
//    *a[0] <=>a[0][0]     *a[1] <=> a[1][0]       *(a[1]+1 ) <=> a[1][1]
//1、p[0][0]好理解,相当于n[0][0]
//2、*(p[0] + 1):分两步理解(1)p[0] + 1,由于p是一个指针数组p[0]相当于n[0](是个地址),
//然后加1,指向n[0][1],(2)* 取内容,故*(p[0]+1)意思是取p[0]+1地址的内容,即是n[0][1]
//3、(*p)[2],我是这样理解的,首先p是一个二级指针,p[0]指向n的第一行,p[1]指向n的第二行;
//然后*p得到的是n的第一行的首地址,一个地址相当于一个一级指针,和[2]结合,就相当于n[0][2]了
*/

/*
#include<iostream>
using namespace std;
struct A{
    A() {}
    ~A() {}
    int m1;
    int m2;
};
struct B:A{
    B() {}
    ~B() {}
    int m1;
    char m2;
    static char m3;
};
struct C{
    C() {}
    virtual~C() {}
    int m1;
    short m2;
};
int main(){
	cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(C);//8 16 12
}
//类的大小只与成员变量(非static数据成员变量)和虚函数指针有关,还要考虑到对齐. 
//那么类A的大小是8个字节;
//类B继承类A,在类A的8个字节基础上,再加上B自己的大小8字节,所以类B大小是16个字节;
//类C是4个字节+4个字节(考虑对齐)+4个字节(指向虚析构函数的指针)=12个字节
*/

//int (*s[10])(int) 表示的是什么?
//函数指针数组,每个指针指向一个int func(int param)的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值