弄死C++

1.传值与传引用与传指针???

使用返回:

传引用:

    局限性:

     !、任何时候都不能使用指向空值的引用,也就是引用应该被初始化。 

          一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,那么,请一定记住:使用指针吧。

         某位程序员这么写:

          int* myPoint=0;

          int &myCaller=*myPoint; //可以吗???这种恐怖的写法。。。。结果的不确定性让你无法左右你的程序,请务必小心这种写法

   优点:    

       !、因为引用必须被初始化,才能使用,这也意味着:使用引用,你不必去检查其合法性,故:引用的效率比指针高。

            所以:引用像老婆一样。。一定终身。。指针像小蜜一样,随人而变。

         为什么重载操作符时需要传引用:仅为了避免不需要的语义误解!比如你重载了[]符号,那么你返回了一个  mya[2]=10,如果返回的是一个指针呢? *mya[2]这样的表达会让你误解:这是一个向量指针。

 

三者比较:

1)“传值”需要对象的构造和析构,可能会很耗时。

2)“传值”对于一般对象而言,传递的大小总是大于“传引用”

3) 对于小对象,例如int,“传值”会比“传引用”更高效。

 

二、名字查找规则、覆盖、隐藏。。。。。

名字查找规则:

当你调用一个函数时,编译器会智能的分析哪个调用的可能性最大,进行搜索加载

Class Father

{

  public virtual Codding(int);

};

Class  Child: public Father

{

  public Codding(void*);  //空指针,说明它可以是任何类型

}

 

void main()

{

Child ch=new Child();

ch.Codding(10);

}

结果是什么:没有找到!!!,程序出错了。。。

为什么呢,因为 名字查找规则的匹配算法在当前范围中查找匹配的函数(也就是Child),找到了一个名字匹配的函数,它不搜索了,同时它发现:参数不一样。所以匹配不了。。。

如果一些编译器给出了“子类的成员函数隐藏了基类的函数”,这就说明了你不应在子类声明名字和基类一样的函数。否则,你不是继承。而是隐藏它了。(除非它们都是虚函数且拥有自己的签名)

那么。。。什么是虚函数呢?简单的说就是通过虚函数,基类可以可以访问子类函数,那么如何访问呢?通过动态练遍的方式。eg:

1.简介

    虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。假设我们有下面的类层次:


class A

{

public:

    virtual void foo() { cout << "A::foo() is called" << endl;}

};


class B: public A

{

public:

    virtual void foo() { cout << "B::foo() is called" << endl;}

};


那么,在使用的时候,我们可以:


A * a = new B();

a->foo();       // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!

===就是因为父类不知道调用的是哪个函数,所以,它才是“虚的”!!。虚函数智能借助指针和引用来实现多态的效果。

那什么事多态呢??

我们假定有这个调用:

void  FunUsed(A *a)

{

a->foo();//不知道调用的是哪个函数,具体调用的是哪个函数,要根据你实现传入的是哪个实例来判断。所以。。是多态!!!它会变幻。。。

}

---多态有什么意义。。简单的抽象的说:是减少耦合度。用户不需要你的类结构,但是他就是可以使用你的函数。

如何实现联编呢?

一种典型的做法是通过这样一个方式:虚连接表

class A

{

public:

    virtual void foo();

};


class B: public A

{

public:

    void foo();    // 没有virtual关键字!

};


class C: public B  // 从B继承,不是从A继承!

{

public:

    void foo();    // 也没有virtual关键字!

};


     这种情况下,B::foo()是虚函数,C::foo()也同样是虚函数。因此,可以说,基类声明的虚函数,在派生类中也是虚函数,即使不再使用virtual关键字。

     但是一个类的虚函数被其构造函数或者析构函数调用的时候,他们就变成普通函数了。也就是说在这两种函数中,自己就无法多态了。。、

     class A

   {

      A()

      {football();}

      ~A();

     virtual void football(): 

     };

    class B

     {

       virtual void football();

     }

     void main()

     {

       A* myA=new B(); //无论如何调用的都是 A.football();

    }

 

在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,就可以将对象的行为抽象化。


    以设计模式[2]中Factory Method模式为例,Creator的factoryMethod()就是虚函数,派生类override这个函数后,产生不同的Product类,被产生的Product类被基类的AnOperation()函数使用。基类的AnOperation()函数针对Product类进行操作,当然Product类一定也有多态(虚函数)。

  另外一个例子就是集合操作,假设你有一个以A类为基类的类层次,又用了一个std::vector<A *>来保存这个类层次中不同类的实例指针,那么你一定希望在对这个集合中的类进行操作的时候,不要把每个指针再cast回到它原来的类型(派生类),而是希望对他们进行同样的操作。那么就应该将这个“一样的操作”声明为virtual。


    现实中,远不只我举的这两个例子,但是大的原则都是我前面说到的“如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的”。这句话也可以反过来说:“如果你发现基类提供了虚函数,那么你最好override它”。

隐藏只有两点:

    当父类与子类的函数名、且参数一样并且没有virtual 修饰,那么父类的函数被隐藏。(废话,这个就是你子类的函数,可以找到且参数一样,何必去父类那再寻找)

   当父类和子类的函数名一样,但是参数不一样,有virtual修饰,那么父类被隐藏(名字查找规则)。

 

 

函数指针:

函数指针的声明:

int (*pFun)(int temp); //定义了一个指向函数的指针

如何应用:

code:

int printInt(int i)

{

cout<<"the number is "<<i<<endl;

}

void main()

{

int (*myPrintInt)(int temp);

(*myPrintInt)=printInt; //赋值, printInt就是首地址,这和数组类似

cout<<"输出结果,用C++的方式"<<myprintInt(5)<<endl;

cout<<"输出结果,用C的方式"<<(*myprintInt)(3)<<endl;

但是,一般情况下,是使用typedef 来定义函数指针。

如下:

如上面那边我们会这么写:

typedef int (*myPrintInt)(int temp); //这部分就定义了一个函数指针,类型名为 myPrintInt

myPrintInt myPrintInt1; //这里就定义了一个myPrintInt1的函数指针

-----所以通过这种方式,在定义多个的函数指针的时候,效果好很多。。。。

 

 

}

 

~~~~(>_<)~~~~ 。。加了那么多。。一个浏览器死掉。。

刚刚写了一些typedef 来判断结构体是否含有某个函数的方式。。。算了。。

接下去写:

STL. 摘自:​​http://www.stlchina.org/twiki/bin/view.pl/Main/STLPracticalGuide​​ 的一个程序

 always wanted to write one and here is my golden 24 karet opportunity: a hello world program. 这个程序把一个字符串转换为一个字符vector,然后以逐个字符显示整个字符串。vector就像是盛放变长数组的花园,在STL所有容器中,大约有一半是基于vector的,故可以这么说,尚若你掌握了这个程序,那么你就理解了整个STL的一半了

 

// Program: Vector Demo 1
// Purpose: 用于演示STL vector
// #include "stdafx.h" - 如果你使用预编译需要包含此文件[[#ExplainIn2][注2]]
#include <vector> // STL vector 头文件. 注意,并没有".h"
#include <iostream> // 需要用到 cout
using namespace std; // 确保命名空间是 std
char* szHW = "Hello World";
// 众所周知,这是个以NULL结尾的字符数组
int main(int argc, char* argv[])
{
vector <char> vec; // 一个字符类型的vector(相当于STL中的数组)
// 为字符vector定义迭代器
vector <char>::iterator vi;
// 初始化字符vector,循环整个字符串,把每个字符放入vector中,直至字符串末尾的NULL字符
char* cptr = szHW; // Hello World 字符串的首地址
while (*cptr != '\0')
{ vec.push_back(*cptr); cptr++; }
// push_back 函数把数据插入vector的最后
// 把存在STL数组中的每个字符打印到屏幕上
for (vi=vec.begin(); vi!=vec.end(); vi++)
// 这就是在STL中循环的标准判断方式- 经常使用 "!=" 而不是 "<"
// 某些容器可能并没有重载操作符 "<" 。
//begin()和end()会得到vector的开头和结尾两个元素的迭代器(指针)
{ cout << *vi; } // 使用间接操作符(*)从迭代器中取得数据
cout << endl; // 输出完毕,打印 "\n"
return 0;
}


push_back 是用来向vector或deque容器中插入数据的标准函数。insert是类似功能的函数,适用于所有容器,但用法更复杂。end()实际上表示在最后的位置再加一,以便循环可以正常执行 - 它返回的指针指向最靠近数组界限的数据。就像普通循环中的数组,比如for (i=0; i<6; i++) {ar[i] = i;} ——ar[6]是不存在的,在循环中不会达到这个元素,所以在循环中不会出现问题。


 STL的排序

​http://www.stlchina.org/twiki/bin/view.pl/Main/STLSortAlgorithms​

如果你需要自己定义比较函数,你可以把你定义好的仿函数(functor)作为参数传入。每种算法都支持传入比较函数。以下是所有STL sort算法函数的名字列表:

​函数名​

​功能描述​

sort

对给定区间所有元素进行排序

stable_sort

对给定区间所有元素进行稳定排序

partial_sort

对给定区间所有元素部分排序

partial_sort_copy

对给定区间复制并排序

nth_element

找出给定区间的某个位置对应的元素

is_sorted

判断一个区间是否已经排好序

partition

使得符合某个条件的元素放在前面

stable_partition

相对稳定的使得符合某个条件的元素放在前面

 

1.2 sort 中的比较函数

当你需要按照某种特定方式进行排序时,你需要给sort指定比较函数,否则程序会自动提供给你一个比较函数。




vector <  > vect;

sort(vect.begin(), vect.end());

sort(vect.begin(), vect.end(), less<>() );


上述例子中系统自己为sort提供了less仿函数。在STL中还提供了其他仿函数,以下是仿函数列表:

​名称 ​

​功能描述 ​

equal_to

相等

not_equal_to

不相等

less

小于

greater

大于

less_equal

小于等于

greater_equal

大于等于

需要注意的是,这些函数不是都能适用于你的sort算法,如何选择,决定于你的应用。另外,不能直接写入仿函数的名字,而是要写其重载的()函数:

less<int>()
greater<int>()


当你的容器中元素时一些标准类型(int float char)或者string时,你可以直接使用这些函数模板。但如果你时自己定义的类型或者你需要按照其他方式排序,你可以有两种方法来达到效果:一种是自己写比较函数。另一种是重载类型的'<'操作赋。

 

 

 再来说一点:

C++的内联函数

为什么需要内联函数:因为我们在调用、执行函数的时候,其实是一个奔波的过程,函数的调用过程是将函数要执行的顺序保存到一张表中,函数执行完后,再转去执行其执行前的函数,这要求保护现场并记忆执行的地址,对于大型的程序,这势必会影响效率

内联函数的定义:

inline int  add(int x, int y)

{

return x+y;

}

他是在编译的时候被替代,而不是在运行的时候被调用。

内联定义的规则:

    1.  类内为内联函数,类外委非内联函数,(所以要求短函数在类内,长函数在类外)---具体的说应该是在类声明的文件中定义的函数都被弄成内联函数。如果是类外的,一样要加上inline

----例如:

     我们在类中的变量一般声明为私有,但是外部读取,那么我们需要的就是提供给调用的接口,这些接口设计成内联的会更好。

 2.可以为类外定义的函数指定 inline 关键字,强行为内联函数。

  3.在内联函数内不允许用循环语句和开关语句。

  4.内联函数的定义必须出现在内联函数第一次被调用之前。