C&C++的常见知识点

一.&和*的知识点

    1.在定义时,*是一个标识符,声明该变量是一个指针,比如说int *p,那么p就是一个指向int型的指针。
在调用时,*p是指指针p指向的那个变量,比如说之前有int a=5;int *p=a; 那么p的值是a的地址,也就是指针p指向a,*p则等于a的值,即*p=5.

    2.在定义时,&是一个引用。比如说有定义int a=5;再定义int b=&a;那么这里的b则引用a的值,即b=5,而再给b赋值:b=10,a的值也会成为10.

针对函数定义里的参数而言吧,因为这里的这两者比较相似:
举几个简单例子:先定义有int x=0;和int *p=x;
1、若定义函数: void fun_1(int a){ a=5;} ,
则调用:fun_1(x); 之后,x还等于0;因为fun_1函数只改变了形参a的值,a只是fun_1函数里的局部变量,调用fun_1(x)相当于是“a=x;a=5;”,x没变;
2、若定义函数:void fun_2(int &a){ a=5;} ,(这里的参数a是引用)
则调用:fun_2(x); 之后,x等于5;因为这里的a引用了x的值;
3、若定义函数:void fun_3(int *a){ *a=5;} , (这里的参数a是指针)
则调用:fun_3(p); 之后,x也等于5;因为fun_3函数的参数a是一个指针,相当于a=p;*a则与*p指向同一地址,改变*a即改变*p即x

二.关键字explicit

      C++提供了关键字explicit, 阻止 不应该允许的经过转换构造函数进行的隐式的发生,简言之,就是声明为explicit的构造函数不能在隐式转换中使用。

C++中,一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数),承担了两个角色。一个是构造器,另一个是默认且隐含

的类型转换操作符。

       所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候 编译器就自动调用这个构造器, 创建一个AAA的对象。这样看起来好象很酷, 很方便。 但在某些情况下(见下面权威的例子), 却违背了我们(程序员)的本意。 这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用。
 代码示例:
#include<iostream>
using namespace std;
class Test1{

public:
    Test1(int m){
       m=m;
       cout<<"test1:"<<m<<endl;
    };
private:
    int m;
};

class Test2{

public:
    explicit Test2(int m){
       m=m;
       cout<<"test2:"<<m<<endl;
    }
private:
    int m;
};

void main(){
      Test1 test1=12;//隐式调用其构造函数,成功
      Test1 test2(12);;//显式调用成功

      Test2 test3=12;//编译错误,不能隐式调用其构造函数
      Test2 test4(12);;//显式调用成功
}
三.关键字.virtual  
     C++通过虚函数实现多态。“无论发送消息的对象属于什么类,他们均发送具有同一形式的消息,对消息的处理方式
可能随接手消息的对象而变”的处理方式称为多态。
概念:1,以继承为前提   2,在父类中用virtual修饰函数   3,在子类中重写该虚函数
作用:1,以父类的引用作为函数的参数类型 2,调用该函数传递子类对象   3,在函数中可以通过该父类的引用调用到子类中重写的虚函数。
        只要是学过C++的人都知道在类Base中加了Virtual关键字的函数就是虚拟函数(例如函数print),于是在Base的派生类Derived中就可以通过重写虚拟函数来实现对基类虚拟函数的覆盖。当基类Base的指针point指向派生类Derived的对象时,对point的print函数的调用实际上是调用了Derived的print函数而不是Base的print函数。这是面向对象中的多态性的体现。
示例代码:
 
  class 
   Base   
  {   
  public 
  :Base(){}   
  public 
  :   
  virtual 
    
  void 
   print(){cout<< 
  "Base" 
  ;}   
  };   
  class 
   Derived: 
  public 
   Base   
  {   
  public 
  :Derived(){}   
  public 
  :   
  void 
   print(){cout<< 
  "Derived" 
  ;}   
  };   
  int 
   main()   
  {   
  Base *point= 
  new 
   Derived();   
  point->print();   
  }    
  //---------------------------------------------------------  
    
  Output:   
  Derived   重载和覆盖的区别: 
  
1.重载的几个函数必须在同一个类中     覆盖的函数必须在继承关系的不同类中   
2.覆盖的几个函数必须函数名,参数,返回值都相同。
3.覆盖的函数前必须加关键字virtual    重载和Virtual没有任何瓜葛,加不加都不影响重载的运作。

关于C++的隐藏规则(引用自《高质量C++/C 编程指南》林锐 2001){错误理论,不要相信}:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual

关键字,基类的函数将被隐藏(注意别与重载混淆)。

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

这里,林锐博士好像犯了个错误。C++并没有隐藏规则,林锐博士所总结的隐藏规则是他错误地理解C++多态性所致。下面请看林锐博士给出的隐藏规则的例证:

示例代码:
 
  
  1. #include<iostream> using namespace std; class Father{ public:     virtual void i(float m){ cout<<"Father:i"<<endl; };     void j(float m){ cout<<"Father:j"<<endl; };     void k(float m){ cout<<"Father:k"<<endl; };      }; class Son:public Father{ public:     virtual void i(float m){ cout<<"Son:i"<<endl; };     void j(int m){    cout<<"Son:j"<<endl; };     void k(float m){  cout<<"Son:k"<<endl; }; }; void main(){     Son son;     Father* father=&son;     Son* son1=&son;     //i()     father->i(3.14f);     son1->i(3.14f);          //j()     father->j(3.14f);     son1->j(3.14f);     //k()     father->k(3.14f);     son1->k(3.14f); }

        林锐博士认为bp 和dp 指向同一地址,按理说运行结果应该是相同的,而事实上运行结果不同,所以他把原因归结为C++的隐藏规则,其实这一观点是错的。决定bp和dp调用函数运行结果的不是他们指向的地址,而是他们的指针类型。“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”(C++ Primer 3rd Edition)。pb是基类指针,pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关,所以pd的所有函数调用的结果都输出Derived::是完全正常的;pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的,所以有virtual的f函数调用输出Derived::,其它两个没有virtual则还是输出Base::很正常啊,nothing surprise! 

     所以并没有所谓的隐藏规则,虽然《高质量C++/C 编程指南》是本很不错的书,可大家不要迷信哦。记住“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”。






















猜你喜欢

转载自blog.csdn.net/slqSLQSHILIQIANG/article/details/72778308