C++语法基础

 

                               C++语法基础

                       本人Linux下C++开发,特将以前的文档晒晒.

            (本人QQ:345168054)
一。C++中的保留字

 1。源自C:
  asm auto
  bool break
  case char const continue
  do double default
  else enum extern
  for float
  goto
  if int short long
  main
  register return
  signed static sizeof switch  struct
  union unsigned
  void volatile
  while

 2。C++专有:
  bool
  delete
  false  / true
  friend
  inline
  namespace  new
  operator
  private protected public
  this throw try typename template
  using virtual

二。C++中的特殊符号:

 ~  :按位取反运算符,析构函数标志符;

 *  :指针运算符,取内容运算符,算术运算符乘法符号;

 .  : 成员运算符;

 &  : 取地址运算符,引用标志符;

 :: :域作用运算符,一元作用域运算符;
        Exam   ::PI


三。基本符号:
  字母:大小写a-z,A-Z (52个);
  数字:0-9 10个;
  特殊字符:空格,!,#,%,^,_,<,> /,\,|,',",(),[],{};


四。标识符:
  1.只能由数字、字母与下划线构成,并且数字不能开头,下划线不能开头;
  2。标识符区分大小写;
  3。标识符不能使用关键字,也不能使用函数名;
  4。标识符中间不能有空格;
  5。标识符长度有限定,一般不要超过32个字符;

五。类的定义及说明

1.

class 类的名称
{
private:
   事物属性;

public:
   事物的功能;
};

class T
{
 private:
  int hour,minute,second;

 public :
  //void showTime();

  void showTime()
  {

  } 
};

构造方法:
    其作用是生成类的具体对象是为对象分配空间,并初始化对象的数据成员。

 构造方法/构造函数是类的成员函数,其特殊性在于:
 1。构造方法必须公开(public);
 2。构造方法无返回值;
 3。构造方法必须与类名相同;
 4。构造方法由系统自动隐式调用;
 5。构造方法可以有多个(可以重载);

默认构造方法:
    由系统提供不带任何实现的构造方法。

无参构造方法:
    为了给所有生成的对象合法的相同的初始值。

有参构造方法:
    至少要给出一个初始参数值;

*带默认值参数的构造方法:
    在声明构造方法时,可以同时给出一个或多个需要的初始值。

 1。默认参数值必须从右向左的顺序给出;

 2。不能在有默认参数的右边出现无默认值的参数;
  T(int h,int m,int s);    //ok
  T(int h,int m,int s=0);       //ok
  T(int h,int m=30,int s=0);    //ok
  T(int h=12,int m=30,int s=0); //ok

  T(int h=12,int m,int s);   //error
  T(int h=12,int m,int s=0); //error

 3。如果给构造方法全部参数都指定了默认值,则不可以再重载构造方法。

 4。在声明构造函数时,形式参数名可以省略。
    T(int ,int ,int );        //无默认值构造函数,ok
    T(int =5,int =10,int =20);//有默认值构造函数,ok

 5。如果构造函数在声明时给出了全部参数的默认值,则在定义对象时可以给一个或几个
  实参,也可以不给实参;


什么是引用?
      引用就是对象的别名。
   引用的声明定义:
   int x=10;
   int &rx=x;

1。引用必须在定义时就初始化;
2。引用是唯一的;指的是原对象必须只能有一个,但可以定义同一个对象的多个引用,就象一个人可以有多个别名;
3。引用不会另外多分配存储空间,也不会产生数值的复制;
4。引用一旦定义,就不能更改。

   int x=10;
   int &rx=x;
   int *px;
   px=&x;

指针与引用的区别:
1。指针变量可以指向不同的变量,但引用却是固定不可改变的;
2。指针变量可以在声明后再初始化,但引用必须在声明的同时初始化;
3。通过指针变量可以间接改变被指向变量的内容,但引用不存在间接取值过程,对引用的操作就是对原对象本身的操作。


拷贝构造函数/复制构造函数
     是类的成员函数,是特殊的构造函数。必须有一个参数,且该参数为本类对象的一个引用。

拷贝构造函数的作用:
     用已经存在的对象,生成/构造新的对象,并且该对象具有与已经存在的对象相同的初始值。

<类名>(类名 引用名/ 类名 &引用名){ }
     T(T &t)
  {
  hour = t.hour;
  minute = t.minute;
  second = t.second;
  }

何时调用拷贝构造函数?
 1。用已存在的对象生成另一个对象时;
 2。对象作为函数的参数,函数调用形参与实参结合时;
 3。函数调用且返回一个对象给调用者时;


析构函数:
    是特殊的成员函数,无返回值,与类名相同,作用是清理对象所占用的资源,释放内存空间。
    一个类只能有一个析构函数,并且析构函数不能重载。
 默认的析构函数中是一个空函数,没有任何实现。

 ~类名(){  } //默认的析构函数

浅拷贝与深拷贝

    浅拷贝:
  默认的拷贝构造函数完成的就是浅拷贝,即类的非静态数据成员的值,在用已存在的对象给新对象赋值时,
 原样拷贝一份副本赋给新对象对应的数据成员,包括指针类型的成员。

 潜在的危险:
 1。由于二个对象的指针数据成员都指向同一片内存空间,则一个对象通过指针改变其内容时,另一个对象指针所指向的内存空间
 的内容也随之改变,而这二个对象是独立的,理应不互相影响。

 2。当对象在释放时,会重复释放二个对象所指向的相同的空间。

 深拷贝:
  为了避免上述问题,需要程序员自行定义拷贝构造函数,完成从赋值到重新分配内存的工作。
  既复制基础数据成员的值,同时为新对象的指针变量重新分配内存空间,并复制已经有内容到新空间。


动态分配与释放内存
   C:malloc / free
   C++: new / delete
        是二个运算符。

   new:
      分配内存单元,能自动识别数据类型,并正确分配相应的存储空间,调用相应类的构造方法。
   new运行符有返回值:
   如果分配成功:返回指向该内存空间的首地址;
   如果分配失败:NULL。

 new语法格式:
    TypeName *var = new TypeName;
 TypeName *var = new TypeName(初始值);
 TypeName *var = new TypeName[数组长度];

  delete:
      释放用new分配内存空间。只是释放动态生成的指针所指向内存空间,但不会删除指针变量本身。
   释放对象不同,delete格式也不同。

   释放单个对象:
   delete 指针变量名;

   释放数组指针:
      delete [] 数组指针变量名;
 

数据存储区划分:
 1。静态存储区:全局变量;

 2。栈:局部变量;

 3。堆:也叫自由存储区,程序在执行时动态分配的存储区。

  静态存储区和栈里的内存空间,在程序执行完后可以操作系统自动回收,故不用程序员释放。但在堆上分配空间,则需要
  人工分配,人工释放。Or ,it caues memeory leak.
 
 内存分配的典型例子:
  int *pt;    //pt 这个变量存放在栈里
  pt=new int; //pt指向堆里一块内存
  *pt=10;     //往pt所指向堆里的存储空间里放入数值10
  cout<<*pt<<endl;
  delete pt;  //释放堆上所分配的内存空间,但是pt仍然在栈里
  pt=NULL; //不用的指针变量要给其赋空值,防止野指针
              //退出函数或程序时,pt变量自动被系统回收
 
new/delete使用注意事项:
   1。delete只能释放由new分配的空间,而不能释放其他定义的指针所占用的存储空间;
       exam:
  int *a,b,*pb;
  pb=&b;
  a = new int(5);
  delete a;       //ok
  delete pb;     //error
   2。对每一个用new分配空间的指针变量,只能用delete 释放一次。

   3。可以用delete释放空指针。
      int *px=NULL;
   delete px;//ok


$$$ malloc/free 与  new / delete的区别:
1。malloc/free是库函数,而new /delete是运算运算符;
2。malloc分配内存空间需要转换成目标数据类型,而new 能自动识别数据类型并正确分配相应长度的空间;
3。malloc必须与free配套使用,而new必须与delete配套使用。
   不能用malloc分配空间,用delete释放,也不能由new分配的空间,由free释放。


断言/assert的使用
     assert 是一个宏,定义在头文件<assert.h> / <cassert>

  功能:
  主要用来调试程序,测试变量对象是否存在,条件是否成立。
 
 用法:
  assert(表达式/条件);

  如果表达式/条件成立true,则程序继续运行,不受任何影响;
  如果表达式/条件不成立false,assert发出错误消息,并调用abort函数,终止程序运行。

取消断言:
   #define NDEBUG

   使用断言注意事项:
   1。过多使用断言会影响程序的性能;
   2。每个断言应该只判断一个条件,因为多个条件存在时如果断方失败,则无法直观定位错误;
   3。不能使用断方改变程序执行环境,因为断言只是调试时生效,如果断方条件改变程序环境,则在程序真正运行时会出现问题。
     exam:
      error:
   assert(n++<10);

   ok:
   assert(n<10);
   n++;


友元
   为了高效访问类的私有成员,而提供的一种机制。

友元的定义
 friend 友元对象的名称;

1。非类定义的友元函数;
2。属于另一个类的成员函数;
3。定义一个类全部为另一个类的友元,即友元类;


1。非类定义的友元函数:类C的函数

   不属于任何一个类,独立定义类外的函数。

   定义语法:

    friend <返回值类型> <函数名称> (函数参数表);
    friend void show();

    class A
    {
    friend void show();
    };

A。友元函数不属于任何一个类,故在类外定义时不需要使用类的作用域运算符::;
B。友元不是类的成员函数,不能直接引用类对象的成员名,必须参数传递一个类的对象,再通过对象来引用其成员;
C。如果一个友元函数要访问多个类的成员,则必须把该函数同时声明为多个类的友元函数。
D。友元关系是单向的,不能传递;

2.属于另一个类的成员函数:友元成员
  exam:

   class girl
   {
 private :
  char *name;
  int age;

 public :

  girl(char *n,int a)
  {
   name = new char[stren(n)+1];
   strcpy(name,n);
   age = a;
  }
  void prt(boy &);

  ~girl()
  {
   delete name;
  }
   
   };

   class boy
   {
 private :
  char 8name;
  int age;

 public:
  boy(char *n,int a)
  {
   name=new char[strlen(n)+1];
   strcpy(name,n);
   age = a;
  }

  ~boy()
  {
   delete name;
  }

  friend void girl::prt(boy &);
   };


 3.一个类作为另一个类的友元:友元类
   二个类:A B

   B 在类A中声明为友元类,则类B中所有的成员函数,都是类A的友元函数。

   friend class 类名;//legal
   friend 类名;       //legal  应该出现在被操纵的类中

类的继承
   为的解决代码复用而提供的一种机制。
   让一个类完全具有已有类的数据成员及操作这些的函数,同时还有自己的数据成员及操作这些数据成员的函数。

继承分为单继承与多继承
   一个子类只有一个父类,或者一个父类只派生出一个子类。

   多继承则是一个子类有一个以上父类。

   继承方式:公有继承,保护继承,私有继承

   继承语法格式:
   派生类/子类 :<继承方式> 父类名称[,<继承方式> 父类名称,<继承方式> 父类名称,...]

   exam:

#include <iostream>

using namespace std;

   class Father
   {
 private :
  int id;
  double salary;

 public  :
  Father(){};
  
  Father(int i,double s)
  {
   id=i;
   salary = s;
   cout<<"Father: constructor "<<endl;
  }

  //id,salary
  void show()
  {
   cout<<"father:id="<<id<<" ,salary="<<salary<<endl;
  }

   };

   class Son : public Father
   {
     private :
  char name[10];
     public :
   Son(int i,double s,char *n):Father(i,s)
   {
   strcpy(name,n);
   cout<<"son: constructor"<<endl;
   }
  //name,id,salary
   void print()
   {
    show();
    cout<<"name="<<name<<endl;
   }
   };

   int main()
   {
  Son s(100,4500,"jack");
  s.print();
  return 0;
   }
  
   公有继承时,父类的成员在公有派生类中的引用权限:

    基类        公有成员     私有成员      保护成员
     public      private       protected
 公有派生类  公有成员     不可访问成员  保护成员

 不可访问成员含义:
  1。在基类外不能直接访问;
  2。在派生类内部不能直接访问;

公有派生类示例:
class Base
{
 private:
  int v1;
 public:
  int v2;
  Base(int a=0,int b=0)
  {
   v1=a;
   v2=b;
  }
};

class Derived : public Base
{
 private:
  int v3;
 public :
  int v4;
  Derived(int a=0,int b=0)
  {
   v3=a;
   v4=b;

  }

  void func()
  {
   int sum1=v1+v2+v3+v4;
   int sum2=v2+v3+v4;
  }
};

int main()
{
 Derived ob(5,6);
 ob.v2=8;

 return 0;
}

私有派生
基类的成员在私有派生类中的引用权限:
    基类        公有成员     私有成员      保护成员
     public      private       protected

 私有派生类  私有成员     不可访问成员  私有成员

class Base
{
 private:
  int v1;
 public:
  int v2;
  Base(int a=0,int b=0)
  {
   v1=a;
   v2=b;
  }
};

class Derived : private Base
{
 private:
  int v3;
 public :
  int v4;
  Derived(int a=0,int b=0)
  {
   v3=a;
   v4=b;

  }

  void func()
  {
   int sum1=v1+v2+v3+v4;
   int sum2=v2+v3+v4;
  }
};

int main()
{
 Derived ob(5,6);
 ob.v2=8;

 return 0;
}

派生类可以继承基类所有的成员,但是基类私有的成员对于派生类来说是不可见的,因此也不能被派生类访问。

保护成员的引入
    即protected修饰的成员,同时也是一种继承/派生的方式。

保护派生示例:

class Base
{
 protected:
  int v1;
 public:
  int v2;
  Base(int a=0,int b=0)
  {
   v1=a;
   v2=b;
  }
};

class Derived : public Base
{
 private:
  int v3;
 public :
  int v4;
  Derived(int a=0,int b=0)
  {
   v3=a;
   v4=b;

  }

  void func()
  {
   int sum1=v1+v2+v3+v4;//ok
   int sum2=v2+v3+v4;
  }
};

int main()
{
 Derived ob(5,6);
 ob.v2=8; //error

 return 0;
}

基类的成员在保护派生类中的引用权限:
    基类        公有成员     私有成员      保护成员
     public      private       protected

 公有派生类  公有成员    不可访问成员   保护成员
 私有派生类  私有成员    不可访问成员   私有成员
 保护派生类  保护成员    不可访问成员   保护成员

在派生类中与基类中同名成员的使用:
    派生类中可以定义与基类同名的成员,此时称为派生类成员覆盖了基类的同名成员。

 如果在派生类中引用了与基类同名的成员,则起作用则是派生类中的成员。
 如果在派少类中想引用基类的同名成员,则必须显示地使用类名::的引用方式。

class Base
{
protected :
 int v1;
public :
 int v2;
 Base(int a,int b)
 {
  v1=a;
  v2=b;
 }
}; 

class Derived :public Base
{
private :
 int v2;
public :
 int v3;
 Derived(int a=0,int b=0)
 {
  v2=a;
  v3=b;
 }
 void func()
 {
  int sum1 = v1+v2+v3;//v2为派生类中的成员

  int sum2 = v1+Base::v2+v3; //v2为基类中的成员
 }
};

void main()
{
 Derived ob(5,6);
 ob.v2=8; //error
 ob.Base::v2 =10;
}

具有继承关系类的对象构造及析构调用顺序

构造函数调用顺序:
 1。先调用基类的构造函数;
 2。再调用对象成员所属类的构造函数;
 3。最后调用派生类构造函数;


析构函数调用顺序:
    1。先调用派生类析构函数;
 2。再对象成员所属类的析构函数;
 3。最后调用基类的析构函数;

具有继承关系的类对象构造函数初始化
    如果自定义带参数的构造函数,必须通过初始化列表给直接的父类传递参数,并且参数一一对应。
 派生类构造函数(参数表):基类构造函数(参数表)

 
什么时候用到构造函数的初始化列表?
 1。需要初始化的数据成员是对象时;
 2。需要初始化的成员由const修饰时;
 3。需要初始化的成员是引用时;

示例:
const修饰成员:
#include <iostream>
using namesapce std;

class Base
{

public:
 const int a;
 int b;
 
 Base(int m,int n)
 {
  a=m;
  b=n;
 }

 Base(int m,int n):a(m),b(n)
 {
  cout<<"init...."<<endl;
 }
};

void main()
{
 Base ob(1,2);
 cout<<ob.a<<endl;
 cout<<" "<<ob.b<<endl;
}

 

初始化列表使用场合:
1。当类关系是组合时,作为成员的对象隐式调用构造函数,产生有名对象,此时初始化列表为构造函数传递实参;
2。当类关系是继承时,作为子类组成部分的父类成员需要调用父类构造函数,产生无名对象,此时初始化列表也为构造函数传递实参;
3。可以为类自身的数据成员初始化,尤其是“引用数据成员”和“常量数据成员”的初始化。

初始化列表的好处:
1。在有继承关系时能为基类传递参数;
2。使用初始化列表能省略调用组合对象拷贝构造函数,同时也省略了显示调用组合成员的构造函数而生成临时对象,从而节省空间提高
程序效率。


初始化列表的语法格式:
派生类::派生类名(基类1 形参1,基类2 形参2...基类n 形参n)
     :基类1(参数),基类2(参数)...数据成员名1(参数),数据成员名2(参数)

多继承
    一个类有多个直接的基类/父类,这种现象就叫多继承。

定义多继承的语法
 class 派生类名:<继承方式> 基类名1,<继承方式> 基类名2,<继承方式> 基类名3...
 {
  派生类的新成员;
  .....
 };

派生类构造函数的执行问题:
 先调用基类的构造函数(按这些基类被继承的顺序依次调用);
 再调用对象成员所属类的构造函数(按这些对象成员在类中定义的先后顺序);
    最后派生类的构造函数;


多继承导致的问题:


多继承中的二义性: 
 在多继承中,一个类不可以重复成为另一个类的直接基类,但可以多次成为该类的间接基类,此时访问派生类成员时会出现一个
 对象有多个同名的成员数据或成员函数。这种现象就叫多继承中的二义性。

 1。访问不同基类的具有相同名字成员时出现的二义性;

示例:
class A
{
 public :
 int a;
 void f();
};

class B
{
 public :
  int a;
  void f();
  void g();
};


class C :public A,public B
{
 public :
 void g();
 void h();

};

int main()
{
 C c;
 c.f();
 c.a=10;

 return 0;
}

2。访问共同基类的成员时出会出现二义性。
class A
{
 public :
 int a;
 void g();
};

class B1:public A
{
 private :
 int b1;
};

class B2:public A
{
 private :
 int b2;
};

class C:public B1,public B2
{
 private :
 int c;
 public :
  void f();
};

void main()
{
 C ob;
 ob.a=10;
 ob.g();
}

二义性的解决办法:
 在调用派生类的成员时,必须加上直接父类的作用域来限定成员。
 ob.B1::a,ob.B2::g()

    这种办法比较麻烦,C++提供虚基类的机制解决多继承中的二义性问题。


虚基类
 当在多条继承路径上有一个公共的基类时,这个公共的基类就会产生多个实例(副本),若只想保存这个基类的一个实例,就需要
 虚基类。

   A(a,g())<-B1(b1)<-C(c,f())
   A(a,g())<-B2(b2)<-C(c,f())

   虚基类就是为了解决二义性问题,使公共基类在派生类对象中只产生一个基类的实例。

虚基类的声明:
 在派生类时,用关键字virtual 限定被继承的基类。
 派生类名:virtual <继承方式> <基类名>

虚基类对象的初始化:
1。虚基类的构造函数在非虚基类之前调用;
2。如果在继承的层次中包含多个虚基类,那么虚基类的构造函数按照它们声明的次序调用;
3。如果虚基类由非虚基类派生,则先调用基类构造函数,再调用派生类构造函数。

练习:
class base{...};
class base2{...};
class level1:public base2,virtual public base{...};
class level2:public base2,virtual public base{...};
class toplevel:public level1,virtual public level2{...};
question:
 如果声明toplevel类的对象,构造函数的调用顺序是怎样的?
Answer:
 base,base2,level2,base2,level1,toplevel

使用虚基类注意事项:
1。一个类在派生中可以作为虚基类,也可以作为非虚基类;
2。在派生类对象中,只产生一个同名虚基类的对象,而各个非虚基类会产生各自的对象;
3。虚基类的子对象是由最远派生类的构造函数通过调用虚基的构造函数进行初始化的;
4。最远派生类是指在继承结构中建立对象时所指定的类;
5。派生类构造函数的初始化列表必须列出对虚基类构造函数的调用,若未列出则表示使用该虚基类缺省的构造函数;
6。在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数要优先于非虚基类的构造函数。

20110622
多态基础

常量函数
    用const修饰的成员函数就叫常量函数。
 就是在普通成员定义后面加上const关键字。
 常量函数不能改变类的属性。
 const是函数类型的一部分,所在在函数的声明与实现中都需要注明该关键字。

 常量函数只能操作类中的常量成员,包括常量和常量对象。

虚函数
    定义:
 虚函数就是在基类中声明为virtual并在派生类中重新定义的成员函数,可实现成员动态重载。

 当编译器发现成员函数后,会自动该成员函数作为动态编译处理,即在程序运行时动态选择对华政策原成员执行。

动态编译:
    只能通过指向基类的指针或基类对象的引用来调用相应的虚函数。

 虚函数调用格式:
 指向基类的指针变量名->虚函数(参数表)
 基类对象的引用名.虚函数(参数表)

 Exmp:
 class A
 {
  public :
  void print()
  {
   cout<<"This is A"<<endl;
  }
 };

 class B:public A
 {
  public :
  void print()
  {
   cout<<"This is B"<<endl;
  }
 };


 int main()
 {
  A a;
  B b;
  a.print();
  b.print();

  return 0;
 }

使用虚函数的必要性:
 1.为了方便使用类的多态性,需要在基类中定义虚函数;
 2。在很多情况下,基类本身生成对象是不合理的。

 

虚函数语法格式:
 virtual 返回类型 函数名(参数表);
 虚函数只需要在声明时使用关键字,在实现该函数时可以省略该关键字。

虚函数的使用:
 指向基类的指针在操作其多态的对象时,会根据不同的对象,调用相应的函数,这些函数就是虚函数。
 如果基类的成员函数被指定为virtual,则其派生类中相应的函数会自动成为virtual函数。

//虚函数使用的例子:
class A
 {
  public :
  virtual void print()=0;//纯虚函数

 };

 class B:public A
 {
  public :
  void print()
  {
   cout<<"This is B"<<endl;
  }
 };
 
 class C:public A
 {
  public :
  void print()
  {
   cout<<"This is C"<<endl;
  }
 };

 class D:public A
 {
  public :
  void print()
  {
   cout<<"This is D"<<endl;
  }
 };
 
 int main()
 {
  //A a;
  B b;
  C c;
  D d;

  A* p;
  p=&b;
  p->print();
  p=&c;
  p->print();
  p=&d;
  p->print();

  return 0;
 }

纯虚函数:
    在基类中没有函数实现的虚函数,通常把函数体写成=0;

 如果基类中的函数没有办法实现,或者没有必须实现,而完全需要派生类去实现时,可以把此函数声明为纯虚函数。

 纯虚函数的定义:
 virtual <返回类型><函数名>(<参数表>)=0;

抽象类:
 含有纯虚函数的类,就称为抽象类。

 抽象类不能实例化,不能生成一个抽象类的对象。

多态:
 指相同的对象收到不同的消息,或者不同的对象收到相同的消息时,执行不同动作。

 C++中支持二种多态性:编译时多态,运行时多态。
 编译时多态:通过函数的重载实现;
 运行时多态:通过虚函数实现;

定义虚函数的限制:
1。非类的成员函数不能定义为虚函数;
2。类的成员函数中静态成员函数和类的构造函数也不能定义为虚函数;
3。类的析构函数一般需要定义为虚函数。
   将基类的析构函数声明为虚函数后,当利用delete运算符销毁一个指向派生类对象时,系统会自动调用派生类相应析构函数。
   如果不把基类的析构函数定义成虚函数,则只会调用基类的析构函数,但并没有销毁派生类的对象,并且在很多时候根本未生
   成基类的对象,从而也不需要销毁。
4。当将基类中某成员函数声明为虚函数后,派生类中同名的函数自动成为虚函数;
5。当某个类声明了成员函数为虚函数,则该类中不能再出现函数原型相同的非虚函数(返回值类型同,参数个数、类型及顺序同)
   而且如果该类派生出其他的子类,则子类中也不能出现同名的非虚函数。
6。虚函数只在声明时使用关键字virtual,而在定义时不需要;


多态实现的方式
1。继承;
2。虚函数;

memecpy()函数:

void *memcpy(void *dest,void *source,unsigned n);

功能:
 把source为起始地址的连续n个字节的数据复制到以dest所指向地址的空间内。


所在头文件:<string.h>  <string>

说明:
 1。source dest所指的内存区域不能重叠;
 2。函数返回值是一个指向dest存储区首地址的指针,而不是void。
 3。一定会连续复制n个字节的数据,并不会遇到字符串结束符'\0'就结束。
    strcpy()复制函数就会结束符就返回。
 4。如果原来dest所指向的存储区里有数据,则调用memcpy()函数后,会把前n个字节数据覆盖掉。

char *s="Golden Global view";
char d[];
char *p;

虚函数表
   
 每一个有虚函数的类都有一个虚函数表,该表按虚函数在类中声明的顺序依次保存虚函数的地址。

 每一个带有虚函数类的对象/实例,都含有一个指向本类虚数表的指针,指向类的虚函数表的首地址。
 VirtualTable,vptr.

重载与覆盖

重载:在同一个类中,有多个同名的函数,即函数名相同,但函数参数的个数,类型,顺序不同。
      overload

覆盖:在具有继承关系的基类与派生类中,有相同的函数签名。
      函数覆盖也叫函数重写。
   override.

如果在子类中必须调用基类中被覆盖的方法,如何实现呢?
    派生类对象.基类名::被覆盖的方法名(参数列表);

函数覆盖的隐藏规则:
1。如果派生类的函数与基类的函数同名,但是参数列表不同,此时不论有没有virtual关键字,基类中同名函数将被隐藏;
2。如果派生类的函数与基类的函数同名,并且参数也相同,但是基类的函数没有virtual关键字,则基类中的函数被隐藏。

 

           --有错误希望大家纠正,希望各位为中国的IT创出一片天...

           --为中国之崛起而努力奋斗...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值