/*
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)的函数。
C++小题(二)
最新推荐文章于 2021-11-21 21:48:31 发布