【转】C++总结

本文深入探讨了C++中的关键概念和技术,包括条件与循环语句的使用技巧、内存管理方式及常见错误、函数参数传递机制、类成员函数的重载与覆盖区别、内联函数与宏代码对比、构造与析构顺序、模板类与模板函数的应用以及深拷贝与浅拷贝的区别等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  知识结构:
  1、if,for,switch,goto
  2、#define,const
  3、文件拷贝的代码,strcpy,memcpy,动态生成内存,复合表达式,sizeof,指针的问题
  4、函数参数传递,内存分配方式,内存错误表现,malloc与new区别
  5、类重载、隐藏与覆盖区别,extern问题,函数参数的缺省值问题,宏代码与内联函数区别
  6、构造和析构的次序,String函数定义,函数模板和模板类,C++深拷贝与浅拷贝
  7、线程和进程的区别、ISO/OSI与TCP/IP的层次结构、TCP和UDP区别
  
  
  
  
  
  
  具体实现:
  1、if,for,switch,goto
  1.1 if:
  bool int float pointer char 变量的使用方法
  bool bParam;
  int iParam;
  float fParam;
  int* pParam;
  char cParam;
  if(bParam) ,if(!bParam);
  if(iParam == 0 ),if(iParam != 0 );
  if(fParam>= -0.00001 && fParam <= 0.00001);
  if(pParam == NULL),if(pParam != NULL);
  if(cParam == '/0'),if(cParam != '/0');
  
  if/else/return 的使用方法
  if(condition) 可以等价为 return (condition?x:y);
  {
   return x;
  }
  else
  {
   return y;
  }
  
  1.2 for:
  执行效率问题:
  int row,col,sum;
  int a[100][5];
  for(row=0;row<100;row++) 效率低于 for(col=0;col<5;col++)
  { {
   for(col=0;col<5;col++) for(row=0;row<100;row++)
   { {
   sum = sum+a[row][col]; sum = sum+a[row][col];
   } }
  } }
  
  int i;
  for(i=0;i  { {
   if(condition) for(i=0;i   DoSomething(); DoSomething();
   else }
   DoOtherthing(); else
  } {
   for(i=0;i   DoOtherthing();
   }
  
  for (int x=0;x<=N-1;x++) 直观性差于 for (int x=0;x  
  1.3 switch:
  switch(variable)
  {
   case value1: ...
   break;
   case value2: ...
   break;
   default: ...
   break;
  }
  switch(c)中的c的数据类型可以是int,char,long,unsigned int,bool.
  variable必须是整数或者强制为整数,由于char实际上是ASCII码,所以也可以.
  c不可以是double,float,char*.
  
  1.4 goto:
  goto主要用于
  {...
   {...
   {....
   goto error;
   }
   }
  }
  
  error:
   ...
  
  
  2、#define,const
  #define和const区别
  2.1、#define C语言
   const C语言 C++语言
   const常量有数据类型,编译器会进行类型安全检查,而#define没有数据类型,
   const的常量可以进行调试,但宏常量不能进行调试.
  2.2、const的使用方法
  在全局定义 const float PI=3.1415926
  在类中定义
  class A
  {...
   A(int size);
   const int SIZE;
  };
  A::A(int size):SIZE(size)
  {
   ...
  }
  对参数和函数的定义(const只能修饰输入参数,不能修饰输出参数)
  const int x=1; 表示x的值是1,在程序中不能改变;
  const int* x; 表示x代表的地址所指向的内容是不能改变得;
  int const* x; 与const int* x;的表示的意思一样;
  int * const x; 表示x代表的地址是不能改变的;
  
  当是输入参数时,不需要是void Func(const int i),void Func(const int& i),可以是void Func(int i)
  因为输入参数采用"值传递"(const int i),由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const修饰;
  不用const int& i的原因在于内部数据类型的参数不存在构造、析构的过程,而复制也非常快,"值传递"和"引用传递"的效率几乎相当.
  
  当是输入参数时,不需要是void Func(const A a),void Func(A a),可以是void Func(A& a)或void Func(const A& a)
  不用const A a,A a的原因是函数的效率比较低,因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制和析构过程都需要消耗时间
  最好用const A&a的原因是A&a中的a可以被改变,A&a和const A&a的好处在于都不会产生临时对象,效率高;
  
  const A Func(const A&a )const的好处
  第一个const表示返回的是个内部产生的对象,它不能被修改
  const A Func(...)
  {...}
  const A a=Func(...);//不能是A a=Func(...);
  第二个const表示输入的参数是引用传递,函数内部不会产生临时对象,而且这个对象不能被内部修改
  第三个const表示此函数内部的所涉及的数据成员不能修改
  class Stack
  {
   int m_num;
   int GetCount(void) const;
   int Pop(void);
  }
  int Stack::GetCount(void) const
  {
   m_num++;//编译错误,企图修改数据成员m_num;
   Pop();//编译错误,企图调用非const函数
  }
  
  3、
  3.1 文件拷贝的代码
  #include
  int main(int argc, char* argv[])
  {
   printf("Hello World!/n");
   FILE* in;
   FILE* out;
   in=fopen("d://1.txt","rb");
   out=fopen("d://2.txt","wb");
   char ch=fgetc(in);
   while(!feof(in))
   {
   fputc(ch,out);
   ch=fgetc(in);
   }
   fclose(in);
   fclose(out);
   return 0;
  }
  
  3.2 strcpy代码
  char* strcpy(char* strDest,const char* strSrc)
  {
   if(strDest==NULL||strSrc==NULL) return NULL;
   char* pStr=strDest;
   while((*strDest++=*strSrc++)!='/0)
   NULL;
   return pStr;
  }
  
  3.3 memcpy代码
  void* memcpy(char* strDest,const char* strSrc,size_t size)
  {
   if(strDest==NULL||strSrc==NULL) return NULL;
   if(size<=0) return NULL;
   char* pStr=strDest;
   while(size-->0)
   *strDest++=*strSrc++;
   return pStr;
  }
  
  
  3.4 动态生成内存的代码
  ------------------------------------------
  正确代码:
  void GetMemory(char **p, int num)
  {
   *p = (char *)malloc(sizeof(char) * num);
  }
  char* GetMemory2(int num)
  {
   char* p = (char *)malloc(sizeof(char) * num);
   return p;
  }
  ------------------------------------------
  错误的代码:
  void GetMemory3(char *p, int num)
  {
   p = (char *)malloc(sizeof(char) * num);
  }
  
  ------------------------------------------
  void Test(void)
  {
   char *str = NULL;
   GetMemory(&str, 100); // 注意参数是&str,而不是str
   strcpy(str, "hello");
   cout<< str << endl;
   free(str);
  
   str=NULL;
   str=GetMemory2(100);
   strcpy(str, "hello");
   cout<< str << endl;
   free(str);
  
   str=NULL;
   GetMemory3(str, 100); // str 仍然为NULL
   strcpy(str, "hello"); // 运行错误
   cout<< str << endl;//运行错误
   free(str);//运行错误
  }
  
  3.5 复合表达式
  d = (a = b + c) + r ;
  该表达式既求a 值又求d 值.应该拆分为两个独立的语句:
  a = b + c;
  d = a + r;
  
  if (a < b < c) // a < b < c 是数学表达式而不是程序表达式
  并不表示
  if ((a  而是成了令人费解的
  if ( (a  
  
  3.6 sizeof:
  i.在32位操作系统中,基本数据类型
  类型 字节长度
  char 1
  short 2
  short int 2
  signed short 2
  unsigned short 2
  int 4
  long int 4
  signed int 4
  unsigned int(unsigned) 4
  long 4
  unsigned long 4
  float 4
  double 8
  void* 4 (所有指针类型长度都一样)(char*,int*,float*,double*)
  enum 4
  
  ii.在32位操作系统中,定义或函数中的大小
  char a[]="hello";
  char b[100];
  char *p=a;
  类型 字节长度
  sizeof(a) 6
  sizeof(b) 100
  sizeof(p) 4
  
  void Func(char a[100])
  {
   sizeof(a); //4,这里a是一个指针,所以是4个长度
  }
  
  #pragma pack(1)
  struct A
  {
   int i;
   char j;
  };
  sizeof(A) //5
  
  #pragma pack(1)
  struct A
  {
   int o;
   int j;
   union
   {
   int i[10],j,k;
   };
  
  };
  sizeof(A) //48
  
  #pragma pack(1)
  struct A
  {
   enum day{monring, moon, aftermoon};
  };
  sizeof(A) //1
  sizeof(A::day) //4
  
  3.7 指针的问题
  int *p; //指向一个整数的指针
  int * const p; //指向一个整数的指针,该指针不能被改变
  const int * p; //指向一个整数的指针,该指针指向的内容不能被改变
  int const * p; //指向一个整数的指针,该指针指向的内容不能被改变
  int *p[10]; //一个10个指针的数组,每个指针指向一个整数
  
  ***********************************************************************
  int func(int a)
  {
   a++;
   return a;
  }
  void main(void)
  {
   int (*p)(int a); //指向一个传入参数是整数,传出参数是整数的函数的指针
   p=func;
   int b=(*p)(10);
   printf("%d/n",b);
  
   int (*pp[10])(int a);//含有10个指向一个传入参数是整数,传出参数是整数的函数的指针数组
   for(int i=0;i<10;i++)
   pp[i]=func;
   int c=(*pp[0])(20);
   printf("%d/n",c);
  }
  ***********************************************************************
  main()无法直接调用hello函数,利用两个函数分别返回hello和调用hello,实现了在main()中调用hello。虽然,似乎这个程序显得多余但却很好的说明了如何把指向函数的指针传
  
  递给函数、作为函数的返回。其中call函数利用了void *型指针的灵活机制,使得call的适用性大为增加,这也正是指向函数的指针的优点之一。
  main()
  {
   void *call(char *(*)());
   char *(*RtnFunc())();  
   printf("%s",call(RtnFunc()));
   return 0;
  }
  char *hello()
  {
   return "Hello World!/n";
  }
  
  char *(*RtnFunc())()
  {
   return hello;
  }
  
  void *call(char *(*func)())
  {
   return (*func)();
  }
  
  
  4、
  4.1 函数参数传递
  C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递.
  
  4.1.1 "值传递"的示例程序.由于Func1 函数体内的x 是外部变量n 的一份拷贝,
  改变x 的值不会影响n, 所以n 的值仍然是0.
  void Func1(int x)
  {
  x = x + 10;
  }
  …
  int n = 0;
  Func1(n);
  cout << "n = " << n << endl; // n = 0
  
  4.2.1 "指针传递"的示例程序.由于Func2 函数体内的x 是指向外部变量n 的指
  针,改变该指针的内容将导致n 的值改变,所以n 的值成为10.
  void Func2(int *x)
  {
  (* x) = (* x) + 10;
  }
  …
  int n = 0;
  Func2(&n);
  cout << "n = " << n << endl; // n = 10
  
  4.3.1 "引用传递"的示例程序.由于Func3 函数体内的x 是外部变量n 的引用,x
  和n 是同一个东西,改变x 等于改变n,所以n 的值成为10.
  void Func3(int &x)
  {
  x = x + 10;
  }
  …
  int n = 0;
  Func3(n);
  cout << "n = " << n << endl; // n = 10
  
  4.2 内存分配方式
  分配方式 变量类型 分配特点
  静态存储区域分配 全局变量,static 变量 内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.
  栈分配 函数内局部变量 栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限.
  堆分配(亦称动态内存分配) new ,malloc分配 用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存.
  
  
  4.3 内存错误
  内存分配未成功,却使用了它.
  内存分配虽然成功,但是尚未初始化就引用它.
  内存分配成功并且已经初始化,但操作越过了内存的边界. 例如在使用数组时经常发生下标"多1"或者"少1"的操作.特别是在for 循环语句中,循环次数很容易搞错,导致数组操作越
  
  界.
  忘记了释放内存,造成内存泄露.
  放了内存却继续使用它.
   函数的return 语句写错了,注意不要返回指向"栈内存"的"指针"或者"引用",因为该内存在函数体结束时被自动销毁.
   程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面.
   使用free 或delete 释放了内存后,没有将指针设置为NULL.导致产生"野指针".
  
  
  4.4 malloc与new区别
  malloc 与free 是C++/C 语言的标准库函数,new/delete 是C++的运算符.它们都可用于申请动态内存和释放内存.
  对于非内部数据类型的对象而言,光用maloc/free 无法满足动态对象的要求.对象在创建的同时要自动执行构造函数, 对象在消亡之前要自动执行析构函数.由于malloc/free 是库
  
  函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free.因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以
  
  及一个能完成清理与释放内存工作的运算符delete.注意new/delete 不是库函数.
  
  
  5、
  5.1 类重载、隐藏与覆盖区别
  成员函数被重载的特征:
  (1)相同的范围(在同一个类中);
  (2)函数名字相同;
  (3)参数不同;
  (4)virtual 关键字可有可无.
  覆盖是指派生类函数覆盖基类函数,特征是:
  (1)不同的范围(分别位于派生类与基类);
  (2)函数名字相同;
  (3)参数相同;
  (4)基类函数必须有virtual 关键字.
  #include
  class Base
  {
  public:
   void f(int x) { cout << "Base::f(int) " << x << endl; }
   void f(float x) { cout << "Base::f(float) " << x << endl; }
   virtual void g(void) { cout << "Base::g(void)" << endl;}
   void h(float x) { cout << "Base::h(float) " << x << endl;}
   void k(float x) { cout << "Base::k(float) " << x << endl;}
  };
  class Derived : public Base
  {
  public:
   virtual void g(void) { cout << "Derived::g(void)" << endl;}
   void h(int x) { cout << "Derived::h(int) " << x << endl; }
   void k(float x) { cout << "Derived::k(float) " << x << endl;}
  
  };
  void main(void)
  {
   Derived d;
   Base*pb = &d;
   Derived *pd = &d;
   pb->f(42); // Base::f(int) 42 //重载
   pb->f(3.14f); // Base::f(float) 3.14 //重载
  
   pb->g(); // Derived::g(void) //覆盖
   pd->g(); // Derived::g(void) //覆盖
  
   pb->h(3.14f) // Base::h(float) 3.14 //隐藏
   pd->h(3.14f) // Derived::h(int) 3 //隐藏
  
   pb->k(3.14f) // Base::k(float) 3.14 //隐藏
   pd->k(3.14f) // Derived::k(float) 3.14 //隐藏
  }
  
  5.2 extern问题
  如果C++程序要调用已经被编译后的C 函数,该怎么办?
  假设某个C 函数的声明如下:
  void foo(int x, int y);
  该函数被C 编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字用来支持函数重载和类型安全连接.由于编译后的名字不同,C++程序不能直接调用C
  
  函数.C++提供了一个C 连接交换指定符号extern"C"来解决这个问题.例如:
  extern "C"
  {
  void foo(int x, int y);
  … // 其它函数
  }
  或者写成
  extern "C"
  {
  #include "myheader.h"
  … // 其它C 头文件
  }
  这就告诉C++编译译器,函数foo 是个C 连接,应该到库中找名字_foo 而不是找_foo_int_int.C++编译器开发商已经对C 标准库的头文件作了extern"C"处理,所以我们可以用#
  
  include 直接引用这些头文件.
  
  5.3 函数参数的缺省值问题
  正确方法:
  void Foo(int x=0, int y=0); // 正确,缺省值出现在函数的声明中
  void Foo(int x,int y)
  {
   ...
  }
  错误方法:
  void Foo(int x=0, int y=0) // 错误,缺省值出现在函数的定义体中
  {
   ...
  }
  
  正确方法:
  void Foo(int x, int y=0, int z=0);
  错误方法:
  void Foo(int x=0, int y, int z=0);
  
  5.4 宏代码与内联函数区别
  语言支持关系:
  C 宏代码
  C++ 宏代码 内联函数
  
  宏代码本身不是函数,但使用起来象函数.预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return 等过程,从而提高了速度
  
  .使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应.
  对于任何内联函数,编译器在符号表里放入函数的声明(包括名字、参数类型、返回值类型).如果编译器没有发现内联函数存在错误,那么该函数的代码也被放入符号表里.在调用一
  
  个内联函数时,编译器首先检查调用是否正确(进行类型安全检查,或者进行自动类型转换,当然对所有的函数都一样).如果正确,内联函数的代码就会直接替换函数调用,于是省去了
  
  函数调用的开销.这个过程与预处理有显著的不同,因为预处理器不能进行类型安全检查,或者进行自动类型转换.假如内联函数是成员函数,对象的地址(this)会被放在合适的地方,
  
  这也是预处理器办不到的.
  
  5.5 内联函数使用方法:
  关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用.
  正确使用方法:
  void Foo(int x, int y);
  inline void Foo(int x, int y) // inline 与函数定义体放在一起
  {
  …
  }
  
  错误使用方法:
  inline void Foo(int x, int y); // inline 仅与函数声明放在一起
  void Foo(int x, int y)
  {
  …
  }
  
  
  6、
  6.1 构造和析构的次序
  构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数.析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动
  
  执行析构过程.
  
  
  6.2 String函数定义
  class String
  {
  public:
   String(const char *str = NULL); // 普通构造函数
   String(const String &other); // 拷贝构造函数
   ~ String(void); // 析构函数
   String & operate =(const String &other); // 赋值函数
  private:
   char *m_data; // 用于保存字符串
  };
  
  // String 的析构函数
  String::~String(void)
  {
   delete [] m_data;// 由于m_data 是内部数据类型,也可以写成delete m_data;
  }
  
  // String 的普通构造函数
  String::String(const char *str)
  {
   if(str==NULL)
   {
   m_data = new char[1]; // 若能加NULL 判断则更好
   *m_data = '/0';
   }
   else
   {
   int length = strlen(str);
   m_data = new char[length+1]; // 若能加NULL 判断则更好
   strcpy(m_data, str);
   }
  }
  
  // 拷贝构造函数
  String::String(const String &other)
  {
   int length = strlen(other.m_data);
   m_data = new char[length+1]; // 若能加NULL 判断则更好
   strcpy(m_data, other.m_data);
  }
  
  
  // 赋值函数
  String & String::operate =(const String &other)
  {
   // (1) 检查自赋值
   if(this == &other)
   return *this;
   // (2) 释放原有的内存资源
   delete [] m_data;
   // (3)分配新的内存资源,并复制内容
   int length = strlen(other.m_data);
   m_data = new char[length+1]; // 若能加NULL 判断则更好
   strcpy(m_data, other.m_data);
   // (4)返回本对象的引用
   return *this;
  }
  
  6.3 函数模板和模板类
  函数模板可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计。
  以下这个例子找到输入的两个数中最大的数:(数据类型包括byte,int,long,double等等)
  #inclue
  typedef unsigned char byte
  template
  T max(T x,T y)
  { return x  void main()
  {
   byte a=5,b=4;
   cout<   int c=6,d=7;
   cout<   long e=8,f=9;
   cout<   int g=10,h=11;
   cout<   getchar();
  }
  分析:
   编译器从调用max()时,实参的类型,推导出函数模板的类型参数。例如,对于调用表达式max(x,y),由于实参x为int型,所以推导出模板中类型参数T为int.
  
  模板类
  使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员,某些成员函数的参数,某些成员函数的返回值,能取任意类型.定义一个类模板与定义函数模板的格式类似,
  
  必须以关键字template开始,后面是尖括号括起来的模板参数,然后是类名,其格式如下:
  template
  class 类名
  {
   ···
   ···
   };
  其中template是一个声明模板的关键字,它表示声明一个模板。关键字class表明后面的Type是模板参数。
  模板的使用就是将类模板实例化成一个具体的类,它的格式为:
   类名<实际的类型>对象名;
  例如:
  #include "iostream.h"
  
  template
  class complex
  {
  public:
   T real;
   T image;
   complex(T r,T i){real=r;image=i;}
   T abs() {T t=real*real+image*image;return t;} //在定义体内的使用方法
   T realcomplex();
  };
  
  //如果在定义体外,则需要如下定义
  template
  T complex::realcomplex()
  {
   return real;
  }
  
  void main(void)
  {
   int a=2,b=3;
   complex obj(a,b);
   int c=obj.abs();
   cout<   int d=obj.realcomplex();
   cout<  
   double e=3.0;f=4.3;
   complex obj1(e,f);
   double g=obj1.abs();
   cout<   getchar();
  }
  
  6.4 C++深拷贝与浅拷贝
  如果你拷贝的对象中引用了某个外部的内容(比如分配在堆上的数据),那么在拷贝这个对象的时候,让新旧两个对象指向同一个外部的内容,就是浅拷贝;如果在拷贝这个对象
  
  的时候为新对象制作了外部对象的独立拷贝,就是深拷贝 。
  
  引用和指针的语义是相似的,引用是不可改变的指针,指针是可以改变的引用。其实都是实现了引用语义。
  深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用的时候,当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝。
  
  对于普通类型的对象来说,他们之间的复制是很简单的,例如:
  int a = 10;
  int b =a;
  
  普通对象和类对象同为对象,他们之间的特性有相似之处也有不同之处,类对象内部存在成员变量,而普通对象是没有的,当同样的复制方法发生在不同的对象上的时候,那么系
  
  统对他们进行的操作也是不一样的,就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。
  当一个类没有自定义的拷贝构造函数的时候系统会自动提供一个默认的拷贝构造函数,来完成复制工作。
  
  6.4.1 深拷贝
  //以下代码演示 深拷贝 的方法,以及介绍 自定义拷贝构造函数和系统默认的拷贝构造函数的内在联系
  #include
  #include
  class Test
  {
  public:
   Test(char* temp)
   {
   cout<<"进入构造函数"<   strcpy(str,temp);
   }
   Test(Test &c_t)//这里就是自定义的拷贝构造函数,类似系统自动生成的默认的拷贝构造函数
   {
   cout<<"进入copy构造函数"<  
   //以下这句,如果去掉就不能完成复制工作了,这是复制过程的核心语句 ,这里算 深拷贝
   strcpy(str,c_t.str);
   }
   ~Test()
   {
   cout<<"载入析构函数!";
   cin.get();
   }
  public:
   char str[20];
  };
  
  void test(Test a)
  {
   cout<<"载入test函数"<  }
  int main(int argc,char* argv[])
  {
   Test a("Hello World!"); //这里是生成一个对象
   Test b=a; //这里是调用 拷贝构造函数 ,是 深拷贝
   cout<   cin.get();
   test(b); //这里也是调用 拷贝构造函数 ,是 深拷贝
   return 0;
  }
  
  6.4.2 浅拷贝
  在某写状况下,类体内的成员是需要开辟动态开辟堆内存的,如果我们不自定义拷贝构造函数而让系统自己处理,那么就会导致堆内存的所属权产生混乱,试想一下,已经开辟的一
  
  端堆地址原来是属于对象a的,由于复制过程发生,b对象取得是a已经开辟的堆地址,一旦程序产生析构,释放堆的时候,计算机是不可能清楚这段地址是真正属于谁的,当连续发
  
  生两次析构的时候就出现了运错误。
  //以下代码演示 浅拷贝 的方法,
  #include
  #include
  class Test
  {
  public:
   Test(char* temp)
   {
   cout<<"进入构造函数"<   strcpy(str,temp);
   m_i=0;
   }
   Test(Test &c_t)//这里就是自定义的拷贝构造函数,类似系统自动生成的默认的拷贝构造函数
   {
   static int i=0;
   i++; //主要是这里的i是static类型的,拷贝的时候数据在堆上分配,所以两个对象指向相同的内存地址,及其对应的内容。
   //前面两行应该算是浅拷贝了
  
   m_i=i;
   cout<<"进入copy构造函数 i is "<  
   //以下这句,如果去掉就不能完成复制工作了,这是复制过程的核心语句 ,这里算 深拷贝
   strcpy(str,c_t.str);
   }
   ~Test()
   {
   cout<<"载入析构函数!";
   cin.get();
   }
  public:
   char str[20];
   int m_i;
  };
  
  void test(Test a)
  {
   cout<   cout<<"载入test函数"<  }
  int main(int argc,char* argv[])
  {
   Test a("Hello World!"); //这里是生成一个对象
   Test b=a; //这里是调用 拷贝构造函数 ,这里应该算成是 浅拷贝
   cout<   cin.get();
   test(b); //这里也是调用 拷贝构造函数 ,这里应该算成是 浅拷贝
   cout<   cout<   return 0;
  }
  
  6.4.3 等价问题
  引用本身是对象的别名,和复制并没有关系,所以不会调用拷贝构造函数
  Test &a=Test("Hello World!");
  与
  Test a("Hello World!");
  是等价的。
  
  7、
  7.1 线程和进程的区别
  7.1.1 相同点:
  (a)二者都具有ID,一组寄存器,状态,优先级以及所要遵循的调度策略。
  (b) 每个进程都有一个进程控制块,线程也拥有一个线程控制块。
  (c) 线程和子进程共享父进程中的资源;线程和子进程独立于它们的父进程,竞争使用处理器资源;线程和子进程的创建者可以在线程和子进程上实行某些控制,比如,创建者
  
  可以取消、挂起、继续和修改线程和子进程的优先级;线程和子进程可以改变其属性并创建新的资源。
  7.1.2 不同点:
  (a) 线程是进程的一部分, 一个没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个进程,进程的执行过程不是一条线(线程)的,而是多条线(线程)共同完成
  
  的。
  (b) 启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。
  (c)系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源。对不同进程来说,它们
  
  具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。而一个线程的数据可以直接为其他线程所用,这不仅快捷,而且方便。
  (d) 与进程的控制表PCB相似,线程也有自己的控制表TCB,但是TCB中所保存的线程状态比PCB表中少多了。
  (e) 进程是系统所有资源分配时候的一个基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。
  
  7.2 ISO/OSI与TCP/IP的层次结构
  ISO:International Organization for Standardization ,国际标准化组织
  OSI:Open System Interconnection , 开放系统互连
  TCP:Transmission Control Protocol ,传输控制协议
  IP:Internet Protocol ,互联网络协议
  
  ISO/OSI参考模型 TCP/IP协议模型 所对应PDU(协议数据单元)
  应用层 ……………应用层 …………数据
  表示层 ……………应用层 …………数据
  会话层 ……………应用层 …………数据
  
  传输层 ……………传输层 …………段
  
  网络层…………… 互联网层……… 包
  
  数据链路层 ………网络接口层 ……帧
  物理层 ……………网络接口层 ……比特流
  
  ISO/OSI参考模型与TCP/IP协议模型
  相同点:1、都有应用层、传输层、网络层。
  2、都是下层服务上层。
  
  不同点:1、层数不同。
  2、模型与协议出现的次序不同,TCP/IP先有协议,后有模型(出现早),ISO/OSI先有模型,后有协议(出现晚)。
  
  7.3 TCP和UDP区别
  UDP 与 TCP 的主要区别在于 UDP 不一定提供可靠的数据传输。事实上,该协议不能保证数据准确无误地到达目的地。
  TCP 的目的是提供可靠的数据传输,并在相互进行通信的设备或服务之间保持一个虚拟连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值