C++笔记

放在函数里面的类叫局部类,局部类的限制太大了,几乎很少看到使用

局部类的限制,大致有以下几点
1. 局部类只在其所在的局部域内可见
2. 在定义局部域以外的地方不能(实际上是没有语法)访问局部类的成员
3. 局部类的函数必须定义在类里面
4. 局部类不能还有静态成员
5. 局部类只能访问外围定义的类型名、静态变量或者枚举值,不能访问局部对象
局部类不能作为模板参数,这是 C++ 一个很可惜的限制。
为什么不能用作模板参数,这是因为模板是需要实例化的(不可能在,实例化时,怎么访问这个局部类呢?main()::NE? main::NE?
这其实是第1,2个限制
======================================================================
大家好:
   小弟在写C++代码的时候,遇到这样一个问题,苦思冥想还是不知其原因,敬请大家好好看看,谢谢。
   代码如下:
       #include<iostream.h>
       void main()
        {
         char *a="hello";
         a[0]='x';
         cout<<a<<endl;
        }  

   我这样写,程序的编译是通过的,但是他就是会发生内存错误;
     而当我把char *a="hello";改成char a[]="hello";以数组来定义是正确的,而且内存也是不会发生内存错误,我实在是想不清楚其中的原因。为什么数组可以,而指针回发生错误。
    请各位高手赐教!!!!
         谢谢!!!!
--------------------------
char *a="hello";
你知道这样定义的意思是什么吗?"hello"是一个字符串常量,所以它的内存地址是不可修改的,
char *a="hello";就表示字符指针a是指向这个常量的内存地址的,所以a所指向的内存地址也是不可修改的。但是你这样a[0]='x'; 修改了a所指向的地址的值,所以自然就会使原来的字符的地址没法寻找了。
但是char a[]这样是不同的,这里已经给a另外分配了空间,并且这些空间被常量字符串的值填充了,但是a有自己固定的地址空间,所以对a的内存修改是没有问题的。
=======================================================================

#include <iostream>
using namespace std;
class Grandfather
{
public:
virtual void f()
{
cout << "I am Grandfather!" << endl;
}
};

class Father : public Grandfather
{
private:
void f()
{
cout << "I am father!" << endl;
}
};

class Son : public Father
{
private:
void f()
{
cout << "I am son!" << endl;
}
};
int main()
{
Grandfather* pG = new Son;
Father* pF = new Son;
pF ->f();  //这里通不过似乎还可以理解

pG ->f();  //既然最终是调用子类的成员,为何它不受子类的private关键字约束?
                   //是否在<<Inside C++ Object Model>>里面有答案?
return 0;
}


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

#include <iostream>
using namespace std;
class Grandfather
{
public:
virtual void f()
{
cout << "I am Grandfather!" << endl;
}
};

class Father : public Grandfather
{
private:
void f()
{
cout << "I am father!" << endl;
}
};

class Son : public Father
{
private:
void f()
{
cout << "I am son!" << endl;
}
};
int main()
{
Grandfather* pG = new Son;
Father* pF = new Son;
pF ->f();  //这里通不过似乎还可以理解

pG ->f();  //既然最终是调用子类的成员,为何它不受子类的private关键字约束?
                   //是否在<<Inside C++ Object Model>>里面有答案?
return 0;
}


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

Grandfather* pG = new Son;
Father* pF = new Son;
pF ->f(); //这里通不过似乎还可以理解

pG ->f(); //既然最终是调用子类的成员,为何它不受子类的private关键字约束?

因为类的虚函数的绑定是在运行期间进行的,在编译期间编译器不知道(或者说不能确定)它要绑定的对象,所以编译器只能根据指针指向的类型(静态类型,即定义指针时指定的类型)进行访问权限检查。比如pG,它指向一个Grandfather类型,所以对于pG->f()实际上编译器在编译期间检查的就是Grandfather的f()是否是public的。

可见,上述设计中本来想通过将虚函数的在派生类中的访问权限改为private以便达到禁止用户直接使用此虚函数的目的,然而实际上却达不到设计目的。因此,保持虚函数的访问权限的一致性是一个好的做法。
=======================================================================
类成员禁止在声明的时候进行初始化
=======================================================================
malloc分配的内存必定在其它地方调用free了,否则此人就是一个借钱不还的人,不要再理他了
=======================================================================

#include<iostream.h>
class A
{

 
public:
 virtual void show()
 {
  cout << "this is A" << endl;
 }
};
class B:public A
{
public:
    void show()
   {
    cout << "this is B" << endl;
this->A::show(); //这里
   }
};

void main()
{
 A* a;
 B b;
 
    a=&b;
 b.show();
a->show();        //和这里应该如何分开理解?
 
}


----------------------------------
B是A的一个派生类,也就相当于说B是A的一种特例,比如说苹果是水果的特例
b是B的一个实例,就是说b是一个苹果的实例
a是指向A的指针,也就是说a可以指向水果,而b是苹果当然也是水果,所以a可以指向苹果。
这时,你说 a.吃(),当然就是吃苹果了,而不是吃的其他的水果

#include<iostream.h>
class A
{

 
public:
 virtual void show()
 {
  cout << "this is A" << endl;
 }
};
class B:public A
{
public:
    void show()
   {
    cout << "this is B" << endl;
this->A::show(); //这里
   }
};

void main()
{
 A* a;
 B b;
 
    a=&b;
 b.show();
a->show();        //和这里应该如何分开理解?
 
}


----------------------------------
B是A的一个派生类,也就相当于说B是A的一种特例,比如说苹果是水果的特例
b是B的一个实例,就是说b是一个苹果的实例
a是指向A的指针,也就是说a可以指向水果,而b是苹果当然也是水果,所以a可以指向苹果。
这时,你说 a.吃(),当然就是吃苹果了,而不是吃的其他的水果

总之,多态就是这样的,父类指针可指向子类对象,这时,调用相应的成员函数时,就优先调用子类的了,C++就是这样设计的,不用过多考虑为什么了,反正设计者认为这样用起来更方便,所以就这样设计了
这个例子实际就是说明多态或者说是虚拟函数的用法!
-----------------------------------
 b.show();当然是调用本身的show函数
而a是基类指针,指向派生类的对象,这时a->show();就不会调用基类的虚拟函数而调用派生类的。
在使用虚拟函数时,一定是通过指针调用!
=======================================================================
* C++提供了四种新的类型强制:

static_cast
const_cast
reinterpret_cast
dynamic_cast

1)staic_cast静态强制;

不能在无关的指针之间进行static类型强制

class CAnimal
{
 //...
public:
 CAnimal(){}
};

class CGiraffe:public CAnimal
{
 //...
public:
 CGiraffe(){}
};

int main(void)
{
 CAnimal an;
 CGiraffe jean;
 
 an = static_cast<CAnimal>(jean);//将对象jean强制成CAnimal类型
 return 0;
}

class CAnimal
{
 //...
public:
 CAnimal(){}
};

class CGiraffe:public CAnimal
{
 //...
public:
 CGiraffe(){}
};

int main(void)
{
 CAnimal an;
 CGiraffe jean;
 
 an = static_cast<CAnimal>(jean);//将对象jean强制成CAnimal类型
 return 0;
}

2、const_cast类型强制

const_cast类型强制将一个const变量变成一个非const的等价形式
int main()
{
 const int j = 99;
 int * k;
 
 k = const_cast<int *>(&j);//解除const
 return 0;
}
3、reinterpret_cast运算符

reinterpret_cast运算符用来将一个类型指针转变为另一种类型的指针,也用在将整开型量转为指针,或将指针转为整型量上;

int main()
{
 int j = 10;
 int * ptr = &j;
 char * cptr;

 cptr = reinterpret_cast<char *>(ptr);//将int指针类型转变为char的指针类型

 return 0;
}

4、dynamic_cast运算符

int main()
{
 int j = 10;
 int * ptr = &j;
 char * cptr;

 cptr = reinterpret_cast<char *>(ptr);//将int指针类型转变为char的指针类型

 return 0;
}

4、dynamic_cast运算符

dynamic_cast的主要目的是:

1)它返回派生类对象的地址;
2)它测试基类指针是否指向下一尖括号<>中所指定类型的对象

dynamic_cast是一个运行时类型信息,dynamic_cast运算符将指向派生对象的基类部分的基类指针转变为指向派生对象的派生类指针,dynamic_cast必须严格地指定与派生对象相同的类,或者它返回NULL指针;

class CAnimal
{
 //...
};
class CGiraffe:public CAnimal
{
 //...
};
class CGoat:public CAnimal
{
 //...
};

int main()
{
 CGiraffe gene;
 CAnimal * aptr = &gene;
 CGiraffe * ptr1,* ptr2;
 
 ptr1 = dynamic_cast<CGiraffe *>(aptr);
 ptr2 = dynamic_cast<CGoat *>(aptr);  //return NULL
 
 return 0;


===========================================================================================================
char *p              = "hello";          // 非const指针,非const数据

class CAnimal
{
 //...
};
class CGiraffe:public CAnimal
{
 //...
};
class CGoat:public CAnimal
{
 //...
};

int main()
{
 CGiraffe gene;
 CAnimal * aptr = &gene;
 CGiraffe * ptr1,* ptr2;
 
 ptr1 = dynamic_cast<CGiraffe *>(aptr);
 ptr2 = dynamic_cast<CGoat *>(aptr);  //return NULL
 
 return 0;


===========================================================================================================
char *p              = "hello";          // 非const指针,非const数据

const char *p        = "hello";          // 非const指针,const数据

char * const p       = "hello";          // const指针, 非const数据

const char * const p = "hello";          // const指针,const数据
__________________________________________________________________________________________________________
const char *p;

char const *p;
是一样的
==========================================================================================================
函数名: itoa
功  能: 把一整数转换为字符串
用  法: char *itoa(int value, char *string, int radix);
程序例:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
   int number = 12345;
   char string[25];

   itoa(number, string, 10);
   printf("integer = %d string = %s/n", number, string);
   return 0;
}


函数名: atoi
功  能: 把字符串转换成长整型数
用  法: int atoi(const char *nptr);
程序例:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
   int n;
   char *str = "12345.67";

   n = atoi(str);
   printf("string = %s integer = %d/n", str, n);
   return 0;
}


======================================================================================================
Q : 为什么在拷贝构造函数里,可以直接访问另外一个对象的引用的私有成员?
主要解答者: mylove0618 提交人: mylove0618
感谢: hydland、pink0763、garfield_82、Kerrie
审核者: babysloth 社区对应贴子: 查看
     A : 


函数名: atoi
功  能: 把字符串转换成长整型数
用  法: int atoi(const char *nptr);
程序例:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
   int n;
   char *str = "12345.67";

   n = atoi(str);
   printf("string = %s integer = %d/n", str, n);
   return 0;
}


======================================================================================================
Q : 为什么在拷贝构造函数里,可以直接访问另外一个对象的引用的私有成员?
主要解答者: mylove0618 提交人: mylove0618
感谢: hydland、pink0763、garfield_82、Kerrie
审核者: babysloth 社区对应贴子: 查看
     A : 


======================================================================================================
Q : 为什么在拷贝构造函数里,可以直接访问另外一个对象的引用的私有成员?
主要解答者: mylove0618 提交人: mylove0618
感谢: hydland、pink0763、garfield_82、Kerrie
审核者: babysloth 社区对应贴子: 查看
     A : 

C++  PRIMER中文版本: 
在第26页:定义了一个类: 
class  IntArray{ 
       public: 
           ..... 
       private: 
               int  _size; 
               int  *ia; 

然后在第29页有个拷贝构造函数: 
IntArray::IntArray(const  IntArray  &rhs){ 
       _size  =  rhs._size; 
       ia  =  new  int[_size]; 
for(int  inx=0;ix<_size;++ix) 
         ia[ix]  =  rhs.ia[ix]; 

我的疑问是:_size和ia都是私有的,如何被rhs调用呢?请各位帮忙小弟!!谢谢。 
--------------------------------------------------------------- 
 
我以前也有你这样的疑问,现在总算明白了。 
所谓访问权限(如public,private),是对“类”来说的,不是对“对象”来说的,private访问权限是其它类不能访问,而非这个类的不同对象不能访问。 
其实这也非常合理,类是自己设计的,当然自己也就知道类的内部结构,所以没有必要对自己也进行类的“封装”。 
--------------------------------------------------------------- 
 
私有的意思是其他类不可以访问, 
所以它只可以被本类访问, 
如果本类也不可以访问, 
就不会被任何函数调用了。:) 
--------------------------------------------------------------- 
 
一个问题,为什么成员函数可以访问私有成员呢?结果很显然,如果不能访问,那么私有成员的存在就没有了意义。而对于成员函数中允许访问对象的数据成员,一方面保证了安全性与封装性,另一方面提供方便的操作。第一句话的解释,就是承认只有成员函数可以访问私有成员,这里不涉及友元及派生。这样一来,安全性仍然得到了保证,也完成了封装工作。对于第二句话,试想,如果都得靠接口来实现数据传送,那么操作是否极为不便?既然处于成员函数中,已经保证了足够的安全和封装性,那么这里如果还得借助接口,就有些不合情合理了。作为对数据成员的灵活处理,设计者允许在成员函数中访问对象的私有成员,为使用者提供了很大的方便。这同时也反映了语言的灵活性和原则性。 
--------------------------------------------------------------- 
 
楼上的几位已经说得很清楚了,私有的权限不是对于类中的各个成员说的,所以本类的成员可以访问本类的私有成员,但是一个对象不是这个类的成员,所以不能访问私有成员,这就是为什么需要getxxx()接口的原因。 
--------------------------------------------------------------- 
 
C++的封装是针对程序设计或作的,所以到达类的层次,而类的实例是运行状态,不再功能范畴之类,从使用角度来说,也无须这么做,理由上面几位很清楚了
==================================================================================================
if (0x80==(cTmp & 0x80))
   //就是汉字

汉字的第一个 byte 大于等于 0x80
IsDBCSLeadByte
GBK范围:
1st byte    2nd byte
0x81~0xfe   0x40~0x7e and 0x80~0xfe

BIG5范围:
1st byte    2nd byte
0x81~0xfe   0x40~0x7e and 0xa1~0xfe

关于字符判断(GB 2312-80, Big-5, Shift-JIS, KS C-5601-1987)
简体中文:
字符集:GB 2312-80
代码页:CP 936
Lead byte:0xA1 - 0xFE
Trail byte:0xA1 - 0xFE

繁体中文:
字符集:BIG-5
代码页:CP 950
Lead byte:0x81 - 0xFE
Trail byte:0x40 - 0x7E, 0xA1 - 0xFE

日文:
字符集:Shift-JIS(Japan Industry Standard)
代码页:CP 932
Lead byte:0x81 - 0x9E, 0xE0 - 0xFC
Trail byte:0x40 - 0xfc (except 0x7F)

韩文(Wansung):
字符集:KS C-5601-1987
代码页:CP 949
Lead byte:0x81 - 0xFE
Trail byte:0x41 - 0x5A, 0x61 - 0x7A, 0x81 - 0xFE


GBK范围:
1st byte    2nd byte
0x81~0xfe   0x40~0x7e and 0x80~0xfe

if(char[0]>='/x081'&&char[0]<='/x0fe'&&( (char[1]>='/x040'&&char[0]<='/x07e') || (char[1]>='/x080'&&char[0]<='/x0fe') ) )
{//汉字
}

===============================================================================================
如何 new 一个多维数组

int ** A = new int*[n];
for(int i = 0;i <n;i++ )
{
   A[i] = new int[m];
}

delete

for(int i=0;i<n;i++)
    delete  [] A[i];
delete [] A;


_________________________________________________________________________________________________

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
 int num1,//行数
     num2;//列数

 cout<<"Please enter the number for row and column: "<<endl;
 cin >> num1 >> num2;

 //为二维数组开辟空间
 int **p = new int*[num1];
 for(int i=0; i<num1; ++i)
  p[i] = new int[num2];

 for(int j=0;j<num1;j++)
 {
  for(int k=0;k<num2;k++)
  {
   p[j][k]=(j+1)*(k+1);
   cout<<setw(6)<<p[j][k]<<':'<<setw(8)<<&p[j][k];
  }
  cout<<endl;
 }

 //释放二维数组占用的空间
 for(int m=0;m<num1;m++)
  delete[] p[m];
 delete[] p;

 return 0;
}


_________________________________________________________________________________________________

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
 int num1,//行数
     num2;//列数

 cout<<"Please enter the number for row and column: "<<endl;
 cin >> num1 >> num2;

 //为二维数组开辟空间
 int **p = new int*[num1];
 for(int i=0; i<num1; ++i)
  p[i] = new int[num2];

 for(int j=0;j<num1;j++)
 {
  for(int k=0;k<num2;k++)
  {
   p[j][k]=(j+1)*(k+1);
   cout<<setw(6)<<p[j][k]<<':'<<setw(8)<<&p[j][k];
  }
  cout<<endl;
 }

 //释放二维数组占用的空间
 for(int m=0;m<num1;m++)
  delete[] p[m];
 delete[] p;

 return 0;
}

最好用vector,方便,也不容易出错。

#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
int main()
{
 int i,
     j,
     m, //行数
     n; //列数

 cout << "input value for m,n:";
 cin>>m>>n;
 
 //注意下面这一行:vector<int后两个">"之间要有空格!否则会被认为是重载">>"。
 vector<vector<int> > vecInt(m, vector<int>(n)); 
 for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
   vecInt[i][j] = i*j;
  
 for (i = 0; i < m; i++)
 {
  for (j = 0; j < n; j++)
   cout<<setw(5)<<vecInt[i][j]<<":"<<setw(9)<<&vecInt[i][j];
  cout<<endl;
 }

 return 0;
}


===============================================================================================
·通过#pragma pack(n)改变C编译器的字节对齐方式
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。


===============================================================================================
·通过#pragma pack(n)改变C编译器的字节对齐方式
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

     例如,下面的结构各成员空间分配情况:
struct test
{
     char x1;
     short x2;
     float x3;
     char x4;
};

     结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。更改C编译器的缺省字节对齐方式
     在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
  · 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
     · 使用伪指令#pragma pack (),取消自定义字节对齐方式。

     另外,还有如下的一种方式:
     · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
     · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。

应用实例

  在网络协议编程中,经常会处理不同协议的数据报文。一种方法是通过指针偏移的方法来得到各种信息,但这样做不仅编程复杂,而且一旦协议有变化,程序修改起来也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做,不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,其它程序无需修改,省时省力。下面以TCP协议首部为例,说明如何定义协议结构。其协议结构定义如下:

#pragma pack(1) // 按照1字节方式进行对齐
struct TCPHEADER
{
     short SrcPort; // 16位源端口号
     short DstPort; // 16位目的端口号
     int SerialNo; // 32位序列号
     int AckNo; // 32位确认号
     unsigned char HaderLen : 4; // 4位首部长度
     unsigned char Reserved1 : 4; // 保留6位中的4位
     unsigned char Reserved2 : 2; // 保留6位中的2位
     unsigned char URG : 1;
     unsigned char ACK : 1;
     unsigned char PSH : 1;
     unsigned char RST : 1;
     unsigned char SYN : 1;
     unsigned char FIN : 1;
     short WindowSize; // 16位窗口大小
     short TcpChkSum; // 16位TCP检验和
     short UrgentPointer; // 16位紧急指针
};
#pragma pack() // 取消1字节对齐方式

_____________________________________________________________________________________________
还是我给出正确答案吧:
如果代码:
#pragma pack(8)
struct S1{
    char a;
    long b;
};
struct S2 {
    char c;
    struct S1 d;
    long long e;
};
#pragma pack()
sizeof(S2)结果为24.
成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.
也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.
S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;
S2中,c和S1中的a一样,按1字节对齐,而d 是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是按4字节对齐.成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e按8字节对齐)整除.这样,一共使用了24个字节.
              a    b
S1的内存布局:11**,1111,
              c    S1.a S1.b     d
S2的内存布局:1***,11**,1111,****11111111

这里有三点很重要:
1.每个成员分别按自己的方式对齐,并能最小化长度
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度
3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐
======================================================================================================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值