c++面试宝

来源:互联网 发布:2016网络fps游戏排行榜 编辑:程序博客网 时间:2024/05/22 08:56

在const成员函数中,用mutable修饰成员变量名后,就可以修改类的成员变量了

class A{

int incr() const{ return ++m_Count;}

mutable  int m_count;

}

-----------------------------------------

成员函数都会在自己的形参表中加一个参数:这个参数是指向函数所在类的类型的CONST指针.比如:
class A
{
 private:
  int n;
 public:
  void set()
  {
    n=10;  
  };
};

上边的set函数,是给n赋值的,但是它怎么知道是给哪一个对象的n赋值呢?这就需要在参数中告诉它。编译器为我们作了这些工作。实际生成的set函数可以理解成这样的:
public A::set(A* const this)
{
  this-〉n=10;
}
而我们调用的时候其实是这样的:

A a;
A::set(&a);

理解这个对你理解你的这个问题很关键.如果是const成员函数,编译器所作的这项工作也会改变。它会生成一个指向cosnt对象的const指针。所以你不能通过this来改变它所指向的对象。但是要知道static对象并不需要this指针来改变,所以它和const函数是没关系的。

但是,当我们的对象是const对象时,即const CTest ttt;这时候表示什么意思呢,ttt的内容是不可以改边的,当我们把&ttt作为一个参数传到形参this时,矛盾出现了:ttt是一个常量,其成员不可以被改变;this指针的成员变量是可以改变的。如果我能正确的将ttt 的地址传给this,那么ttt这个常量的值不是可以在this中被改变了吗,所以,编译器是不允许这种情况出现的,就提示错误了。故,const对象不能访问非const成员函数。

    同理,当我们的成员函数是const成员函数时,例:CTest::ttt() const,在编译器解释时会将该函数解释为CTest::ttt(const CTest * const this),this指针及其所指向的内容都不可以被修改,前面提到的矛盾也就不存在了,所以const对象可以访问const成员变量。


  ---------------------------------------------

-  值得注意的是,把一个成员函数声明为const可以保证这个成员函数不修改数据成员,但是,如果据成员是指针,则const成员函数并不能保证不修改指针指向的对象,编译器不会把这种修改检测为错误。例如,

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Name {  
  2. public:  
  3. void setName(const string &s) const;  
  4. private:  
  5.     char *m_sName;  
  6. };  
  7.   
  8. void setName(const string &s) const {  
  9.     m_sName = s.c_str();      // 错误!不能修改m_sName;  
  10.   
  11. for (int i = 0; i < s.size(); ++i)   
  12.     m_sName[i] = s[i];    // 不好的风格,但不是错误的  
  13. }  

  在头文件的类的定义中定义了一个const成员变量c++ 规则:
1、类定义中不能进行初始化,因为头文件中类的定义只是一个声明,并没有分配真正空间,因此变量是不存在的,因此是不能赋值的。
2、const 定义的变量是不能赋值
这可如何是好,声明中不能赋值,声明完还不能赋值。又不能不赋值。
解决方案:
1、在构造函数后的参数初始化列表中初始化
2、将const变量同时声明为 static 类型进行初始化。


------struct {

short a1;

int  a2;

short a3;

}A

sizeof(A) = 12; //struct  以最大类型为单位

char a[]="a\n";  sizeof(a) = 3; //最后一个字符是‘/0’

char *q = (char  *)malloc(100); sizeof(q) = 4;

--------int main(){

int a;

char b;

int c; 

}

printf("0x08x",&a);  显示0x0012ff7c

printf("0x08x",&b); 显示0x0012ff7b

printf("0x08x",&c); 显示0x0012ff80

在VC上智能的排序,而不是依次的排放

---------内联函数的短短几行代码中不包含 for 、while、 switch

const 常量赋值时,必须同时初始化  如: const int a = 4;

---------int (*(*F)(int,int)(int))

F是一个函数的指针,指向的函数是有两个int参数并且返回一个函数指针的函数,这个函数指针指向有一个int参数返回int

---------int a[] = {1,2,3,4};

int *p = (int *)(&a+1);

printf("%d",*(p-1));//输出4;数组名本身就是指针,在加个&就变成个双指针,双指针就是二维数组,加1就是数组整体加一行,p就指向第5个元素3

---------

int *p=new int;
*p = 2;
printf("%d",*p);
delete p;
printf("%d",*p);//迷途指针  输出随意值

---------- 两种方式 :1、feof(fp)有两个返回值:如果遇到文件结束,函数feof(fp)的值为1,否则为0   while(!feof(FP)){...}

2、 EOF是文件结束标志的文件。在文本文件中,数据是以字符的ASCⅡ代码值的形式存放,ASCⅡ代码的范围是0到255,不可能出现-1

,因此可以用EOF作为文件结束标志。   

while((c=fgetc(fp)) != EOF)   

  • {   
  •     printf("%X\n"c);   
  •  

----------char * a = "abc";    char *b[3] = {"aaa","bbb","ccc"};

a[1] = "1"'//出错error

b[1][1] ="2" //出错 error      常量数据区不可以修改;

char **p = new char *[3];  只是分配了3个char类型的指针,并没有给这三个指针分配空间;所以有:

for(i=0;i<3;i++){

fscanf( fp, "%s", cur) ;

p[i++] = cur;   

}   //结果p[0]p[1]p[2] 都指向cur一字符空间,即cur最后一个数据(fp文件最后一行)

--------------------vector<string > a;

a.push_back("aaa");  a.push_back("bbb");    可以有a[1][1],用二维数组的形式表示


-------------------------------------------------------------

//向空的vector插入数据

vector<unsigned int> station;
for(vector<int>::size_type ix= 0; ix != StationNum;++ix){
station.push_back(*(pStationArray+ix));
}

  1. int a[7]={1,2,3,4,5,6,7};  
  2.     vector<int> ivector(a,a+7);

--------------------------------------------

复制构造函数  复制的空间如果是指针的话就复制指针里的地址;即所指的空间不变; 如果是常量就复制值,地址会变;(关键是注意地址和常量)

-------------------------------------

array = 1,6,6,3

for(iter=array.begin();iter!=array.end();iter++){

if(6 == *iter){

iter = array.erase(iter);

iter--;           //可以使iter--;再回到原来的位置上即第二个6;如果不写iter--;则会指向3;

} }

或者直接用  array.erase(remove(array.begin(),array,end(),6) , array.end());

--------------------------------

一个空类,编译器默认产生4个成员函数:默认构造函数,析构函数,拷贝构造函数,赋值函数

常量const 必须在构造函数的初始化列表里面初始化  或者将其设置为static

Base *pBase;    //或者写成:Base *pBase = new Child; 

Child c;

pBase = &c;    //如果~Child不是虚函数,当pBase指针被撤销时,只会调用~Base回收Base的空间,导致内存泄露;当为virtual时,则先调用~Child再调用~Base

析构函数可以是内联函数

在Child类中可以访问父类中的保护成员,但是不可以修改

-----------------------------------

因为类的静态数据成员有着单一的存储空间而不管产生了多少个对象,所以存储空间必须
定义在一个单一的地方如果一个静态数据成员被声明但没有定义时,连接器会报告一个错误。
定义必须出现在类的外部(不允许内联)而且只能定义一次,因此它通常放在一个类的实
现文件中  class A{
static int i;
public:
//};之后,在定义文件中:int A::i=1;  静态成员函数不能访问一般的数据成员,它只能访问静态数据成员,也只能调用其他的静态成员函数。

通常,当前对象的地址( t h i s)是被隐含地传递到被调用的函数的。但一个静态
成员函数没有t h i s,所以它无法访问一般的成员函数。这样使用静态成员函数在速度上可以比
全局函数有少许的增长,它不仅没有传递t h i s所需的额外的花费,而且还有使函数在类内的好
处。用s t a t i c关键字指定了一个类的所有对象占有相同的一块存储空间,函数可以并行使用它,
这意味着一个局部变量只有一个拷贝,函数每次调用都使用它。

--------------------------------------------

A(父)   B(孩子)   

A *pa = new A;
B *pb = dynamic_cast<B*>(pa);    // printf("%p\n%p",pb,(A*)(pb));  输出都是0x00000000;
if(pa==pb)  //隐式转换   相当于 pa = (A*)pb           cout<<"!=";  输出不相等    同时  int(pa) != int(pb)   因为指针pa、pb 的值不同

B *pb = new B;
A *pa = pb;
if(pa==pb)      //隐式转换   相当于 pa = (A*)pb        cout<<"=";  输出相等    int(pa) != int(pb)   因为指针pa、pb 的值不同

---------------------------

struct A{
unsigned char a:1;
char b:3;
unsigned char c:1;
unsigned char d:1;
unsigned char e:2;
};
void main(){
A x;
char *p = (char *)&x;
*p = 0xdb;//  
将db写成二进制 再倒写  ,要注意  unsigned char和char的区别
printf("%d,%d,%d,%d,%d,%d",sizeof(A),x.a,x.b,x.c,x.d,x.e);
getchar();
}

1,1,-3,1,0,3

一、对象复制

#include<stdio.h>
#include<iostream>
using namespace std;
class A{
public:
A(){m_a = 1;m_b = 2;}
~A(){};
void fun(){printf("%d%d",m_a,m_b);}
public:
int m_a;
int m_b;
};
class B{
public:
B(){m_c = 3;}
~B(){};
void funa(){printf("%d",m_c);}
public:
int m_c;
};
int main(){
A a;
B *pb = (B *)(&a);//pb是指向对象的内存地址(这里是a)但是调用的是函数是B的函数
pb->funa();//调用的是申明它的类的函数
cout<<endl;
printf("%p\n",&a);
printf("%p\n",pb);
printf("%p\n",&A::m_a);//显示的是m_a距离对象的偏移量这里是0
cout << &A::fun<<endl;
system("pause");
}




#include<stdio.h>
#include<stdlib.h>
#define FIND(struc,e) (unsigned int)&(((struc *)0)->e)//宏定义
struct student{
int a;
char b[20];
};
int  main(){
student stu;
unsigned int a = FIND(student,b);
printf("%d\n",a);
printf("%p",&student::b);
system("pause");
}

2、指针

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

struct S{
int i;
int * p;
};
void main(){
S s;
printf("s: %p \n",&s);
int *p = &s.i;//这里的p就是s.i的别名,其地址跟s.i一样
printf("*p: %d p: %p\n",*p,p);
p[0]= 4;
p[1]=3;
printf("s.i: %d &s.i: %p\n",s.i,&s.i);
printf("s.p: %d &s.p: %p\n",s.p,&s.p);
printf("*p: %d p: %p\n",*p,p);
s.p=p;
printf("s.p: %p &s.p: %p\n",s.p,&s.p);
s.p[1]=1;
printf("s.p: %p &s.p: %p\n",s.p,&s.p);
// s.p[0]=2; //出错,将0x0000001的地址赋值为2;
printf("s.p: %p &s.p: %p\n",s.p,&s.p);
printf("s.i: %d &s.i: %p\n",s.i,&s.i);
// printf("%d   %p\n",s.i,s.p);
system("pause");
}

3、istringstream用法

istringstream对象可以绑定一行字符串,然后以空格为分隔符把该行分隔开来。

[cpp] view plaincopy
  1. #include<iostream>  
  2. #include<sstream>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     string str, line;  
  7.     while(getline(cin, line))  
  8.     {  
  9.         istringstream stream(line);  //将line这行字符串以中间空格的形式赋给stream
  10.         while(stream>>str)  //每次输出空格中的一个字符串
  11.             cout<<str.c_str()<<endl;  //对string输出时要加上.c_str();
  12.     }     
  13.     return 0;  
  14. }  

测试:
input:
abc   df   e              efgeg      ffg
ouput:
abc
df
e
efgeg
ffg


如何避免内存泄漏
1.运行检测法
定义自己的mallocfree函数,或者对new
delete进行重载,在运行时跟踪记录动态内存
的分配和释放情况
利用专用的检测工具,如BoundsChecker
PurifyPerformance Monitor
2. 利用复杂的程序设计技术(C++)
智能指针技术
C++增加垃圾回收机制(可参考《C++编程
艺术》艺术)

1、栈区(stack— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域BSS - 程序结束后有系统释放

4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区存放函数体的二进制代码。


本来仅仅区别重载与覆盖并不算困难,   但是   C++的隐藏规则使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下: 

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无   virtual   关键字,基类的函数将被隐藏(注意别与重载混淆)   。 
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有   virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)   。 

示例程序   2(a)中: 
(1)函数   Derived::f(float)覆盖了   Base::f(float)。 有virtual
(2)函数   Derived::g(int)隐藏了   Base::g(float),而不是重载。 
(3)函数   Derived::h(float)隐藏了   Base::h(float),而不是覆盖。 

HTTPS和HTTP的区别主要为以下四点:
一、https协议需要到ca申请证书,一般免费证书很少,需要交费。
二、http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
三、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
四、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

左偏树

[性质1] 节点的键值小于或等于它的左右子节点的键值。

[性质2] 节点的左子节点的距离不小于右子节点的距离。

[性质3] 节点的距离等于它的右子节点的距离加1。

我们的印象中,平衡树是具有非常小的深度的,这也意味着到达任何一个节点所经过的边数很少。左偏树并不是为了快速访问所有的节点而设计的,它的目的是快速访问最小节点以及在对树修改后快速的恢复堆性质。从图中我们可以看到它并不平衡,由于性质2的缘故,它的结构偏向左侧,不过距离的概念和树的深度并不同,左偏树并不意味着左子树的节点数或是深度一定大于右子树。




 new 返回指定类型的指针,并且可以自动计算所需要大小

而 malloc 则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针。

malloc 只管分配内存,并不能对所得的内存进行初始化所以得到的一片新内存中,其值将是随机的

有了malloc/free为什么还要new/delete?

1) malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
2) 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。
3) 既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。

-------------------------

当用二维数组或者更高维时 可以用struct结构体形式

struct A{
int a;
int b;
int c;
};

A vect[3];

sort(vect,vect+3,cmp);


------------------

类模板的static成员,所有给定实例化的所有对象都共享一个static成员,如Foo<int>类型的任意对象共享一个static成员 ,Foo<string> 类型的任意对象共享另一个static成员 

所以当试图通过类使用static成员的时候必须应用实际的实例化,如size_t t = Foo<int>::count()

使用static数据成员必须在在类外部出现数据成员的定义,要有template <class T> size_t Foo<T>::ctr = 0; 



C++二维数组new几种应用方法



C++二维数组new应用方式一:

  1. A (*ga)[n] = new A[m][n];   
  2. ...   
  3. delete []ga;  

缺点:n必须是已知

C++二维数组new应用方式二:

  1. A** ga = new A*[m];   
  2. for(int i = 0; i < m; i++)   
  3. ga[i] = new A[n];   
  4. ...   
  5. for(int i = 0; i < m; i++)   
  6. delete []ga[i];   
  7. delete []ga;  

缺点:非连续储存,程序烦琐,ga为A**类型

优点:调用直观,n可以不是已知

C++二维数组new应用方式五:

  1. vector ga;   
  2. ga.resize(m*n);  

方法3,4的结合

C++二维数组new应用方式六:

2的改进版

  1. A** ga = new A*[m];   
  2. ga[0] = new A[m*n];   
  3. for(int i = 1; i < m; i++)   
  4. ga[i] = ga[i-1]+n;  

优点:连续存储,n可以不是已知,析构方便,猜想只需delete [] ga;



0 0