effective c++里面的一些知识
enum string{
x1,
x2,
x3=10,
x4,
x5,
} x;
1、函数外部访问x等于什么?
答:如果是函数外定义那么是0,如果是函数内定义,那么是随机值,因为没有初始化,枚举变量是全局变量的情况下, 枚举值的缺省值是0,不是枚举的第一个值。 其他情况,其值是不定的,而且不限定于所列出的枚举值。全局变量时初始化为0,局部变量时初始化为随机值。
2、索引加一,char每次移动1个字节;short移动2个字节 ;int , long ,float移动4个字节 ;double移动8个字节,如下程序所示:
char str[] = “glad to test something”; //定义字符串
charp = str; //p指向字符串首地址,即字符’g’
p++; //p是char类型,每次移动sizeof(char)字节,故此时p指向 ‘g’的下一个字符 ‘l’
intp1 = reinterpret_cast<int>(p); //指针p被重新解释为整型指针并被赋值给p1
p1++; //p1是int类型, 每次移动sizeof(int)字节,故此时p1 指向 ‘l’后的第四个字符 ‘t’
p = reinterpret_cast<char>(p1); //指针p1被重新解释为字符型指针并被赋值给p
printf(“result is %s\n”, p); //从’t’开始输出字符串,即得到 “to test something”
3、请说一下C/C++ 中指针和引用的区别?
1.指针有自己的一块空间,而引用只是一个别名;
2.使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小;
3.指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象的引用;
4.作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;
5.可以有const指针,但是没有const引用;
6.指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能 被改变;
7.指针可以有多级指针(**p),而引用只有一级;
8.指针和引用使用++运算符的意义不一样;
9.如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。
4、类型标识符 &引用名=目标变量名(引用格式)
5、重载:两个函数名相同,但是参数列表不同(个数,类型),返回值类型没有要求,在同一作用域中
重写:子类继承了父类,父类中的函数是虚函数,在子类中重新定义了这个虚函数,这种情况是重写
6、strcpy是字符串拷贝函数,原型:
char strcpy(char dest, const char *src);
从src逐字节拷贝到dest,直到遇到’\0’结束,因为没有指定长度,可能会导致拷贝越界,造成缓冲区溢出漏洞,安全版本是strncpy函数。
strlen函数是计算字符串长度的函数,返回从开始到’\0’之间的字符个数。
7、请你来说一下C++里是怎么定义常量的?常量存放在内存的哪个位置?
答:对于局部常量,存放在栈区;对于全局常量,编译期一般不分配内存,放在符号表中以提高访问效率;字面值常量,比如字符串,放在常量区。
8、虚函数:简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。基类的析构函数被定义为虚函数,在main函数中用基类的指针去操作继承类的成员,释放指针P的过程是:只是释放了继承类的资源,再调用基类的析构函数.调用dosomething()函数执行的也是继承类定义的函数. 如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销.当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间.所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数.
9、在c++中,当申明变量int p 的时,表示p是一个储存地址的变量;比如int p=0,表示p指向地址为00000000的地址单元。当申明指针p之后,再用*p表示p指向的储存空间的内容;&表示取变量的地址;如下程序所示:
#include<iostream>
using namespace std;
int main(){
int a=123;
//&a表示a在内存中的地址,也就是123在内存中的地址
cout<<"a: "<<a<<endl<<"a's address:"<<&a<<endl;
//此时p是一个指针,指向a所在的位置
int *p=&a;
cout<<"p: "<<p<<endl;
//声明p之后,在p之前添加*表示p指向内存的值
cout<<"p's value: "<<*p<<endl;
//同时p也是 一个变量,在内存中也有一个地址储存它,但其地址不是a的地址
cout<<"p's address: "<<&p<<endl;
//试试*&组合使用是什么效果
cout<<"*&p: "<<*&p<<endl;
//&p是一个内存地址,*&p表示&p指向地址内存空间的值,在这里表示a的地址
cout<<"**&p: "<<**&p<<endl;
//刚才我们已经知道*&p是a的地址,那么**&p就表示a的值
return 0;}
10、编译器会暗自为class创建缺省构造函数 copy构造函数、copy assignment操作符、析构函数,但是如果内部含有reference或者const成员,无法自动定义copy assignment操作符,并且如果基类的=操作符为私有的,那么派生类将不能调用它,派生类就不能生成=操作符。如何防止自动生成=操作符和copy构造函数,我们只需要自行定义,并且将其声明为private即可,这样就不能被调用,或者使该类继承一个nocopy的基类。
11、polymorphic(多态)base classes 应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。classes 的设计目的如果不是作为base classes使用,或者是不为了具备多态性,析构函数就没必要声明成虚函数。
12、设计operator=时防止自我赋值出错的三个办法:1、比较赋值双方的地址。2、精心周到的语句顺序,避免先删除再赋值。3、利用copy_and_swap手段。
13、冒号后面跟的是赋值,这种写法是C++的特性。
A( int aa, int bb ):a(aa),b(bb)
{
}
相当于
A( int aa, int bb )
{
a=aa;
b=bb;
}
14、copying函数应该确保复制“对象内的所有成员变量”以及“所有的base class成分”。不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。
15、
#include <iostream>
using namespace std;
class A
{
private:
int n;
public:
A(int m):n(m)
{
}
~A(){}
};
int main()
{
A a(1); //栈中分配
A b = A(1); //栈中分配
A* c = new A(1); //堆中分配
delete c;
return 0;
}
第一种和第二种没什么区别,一个隐式调用,一个显式调用,两者都是在进程虚拟地址空间中的栈中分配内存,而第三种使用了new,在堆中分配了内存,而栈中内存的分配和释放是由系统管理,而堆中内存的分配和释放必须由程序员手动释放,所以这就产生一个问题是把对象放在栈中还是放在堆中的问题,这个问题又和堆和栈本身的区别有关,这里面有几个问题:
1.堆和栈最大可分配的内存的大小
2.堆和栈的内存管理方式
3.堆和栈的分配效率
首先针对第一个问题,一般来说对于一个进程栈的大小远远小于堆的大小,在linux中,你可以使用ulimit -s (单位kb)来查看一个进程栈的最大可分配大小,一般来说不超过8M,有的甚至不超过2M,不过这个可以设置,而对于堆你会发现,针对一个进程堆的最大可分配的大小在G的数量级上,不同系统可能不一样,比如32位系统最大不超过2G,而64为系统最大不超过4G,所以当你需要一个分配的大小的内存时,请用new,即用堆。
其次针对第二个问题,栈是系统数据结构,对于进程/线程是唯一的,它的分配与释放由操作系统来维护,不需要开发者来管理。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时,这些存储单元会被自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,不同的操作系统对栈都有一定的限制。 堆上的内存分配,亦称动态内存分配。程序在运行的期间用malloc申请的内存,这部分内存由程序员自己负责管理,其生存期由开发者决定:在何时分配,分配多少,并在何时用free来释放该内存。这是唯一可以由开发者参与管理的内存。使用的好坏直接决定系统的性能和稳定。
由上可知,但我们需要的内存很少,你又能确定你到底需要多少内存时,请用栈。而当你需要在运行时才知道你到底需要多少内存时,请用堆。
最后针对第三个问题,栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率 比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在 堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。由上可知,能用栈则用栈。
#include <stdio.h>
#include <stdlib.h>
void main()
{
int n,*p,i,j,m;
printf("本程序可对任意个整数排序;\n");
printf("请输入整数的总个数: ");
scanf("%d",&n);
p=(int *)calloc(n,sizeof(int)); //运行时决定内存分配大小
if(p==0) {
printf("分配失败!\n");
exit(1);
}
16、为防止资源泄漏发生,请使用RAII对象,它会自动释放资源,在auto_ptr与shared——ptr中,一般倾向于使用后者。
17、一般情况下,内置类型和STL的迭代器和函数对象使用pass_by_value比pass_by_refence_to_const更快。其它情况尽量以后者替换前者,前者 通常比较高效,并且可以避免切割问题。不要使用pointer以及REFERENCE指向一个local stack对象。protected并不比public更具有封装性。