从template到static

    xiaoxiao2021-03-25  164

    1:函数模板

    一个函数实现一个类似公式的功能

    template<typename T>

    int compare(const T &a, const T &b )

    {

    if(a < b) return -1;

    if(a > b) return 1;

    return 0;

    }

    注释:在模板定义中,模板参数不能为空

    2:类模板

    定义:可以从它实例化出特定的类,类模板的定义以关键字template开始, 后跟尖括号对<>,其内为一个逗号分隔一个或多个模板参数的列表,随后是类的定义。

    一个模板就是一个创建类或函数的蓝图或者说公式。

    template<class 模板参数表> class 类名{ // 类定义...... };

    其中,template 是声明类模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个,可以是类型参数 ,也可以是非类型参数。类型参数由关键字class或typename及其后面的标识符构成。非类型参数由一个普通参数构成,代表模板定义中的一个常量。

    类模板实例化:

    例子:

    template<class Type> class Graphics{}; void f1(Graphics<char>);// 仅是一个函数声明,不需实例化 class Rect {   Graphics<double>& rsd;// 声明一个类模板引用,不需实例化   Graphics<int> si;// si是一个Graphics类型的对象,需要实例化类模板 } int main(){   Graphcis<char>* sc;// 仅声明一个类模板指针,不需实例化   f1(*sc);//需要实例化,因为传递给函数f1的是一个Graphics<int>对象。   int iobj=sizeof(Graphics<string>);//需要实例化,因为sizeof会计算Graphics<string>对象的大小,为了计算大小,编译器必须根据类模板定义产生该类型。 } 应用场景:

    需要最大化复用代码,模块法开发时,比如我们在服务器开发时,,如实现一个线程池,而可能我们需要实现很多不同的时间和场景,为了使线程池的代码能像库一样被多次使用,就可以定义一个线程池类模板,参数是处理逻辑任务的类。

    实际调用只需要:threadpool <task>  * pool;

    网上参照:http://www.cnblogs.com/assemble8086/archive/2011/10/02/2198308.html

    当我们设计类模板时,我把构造函数设计成了私有的private,,由于private 封装了类的实现细节,(注释:class和struct唯一区别就是默认的访问权限),这就造成了调用 该类时无法实例化对象,只能通过成员函数实现实例,比如我们的线程池类,我们可以实现一个create函数来实现类的实例化:

    public:

    static threadpool<T> * create (int lintenfd, int process_number );

    C++构造函数/析构函数 设置成private的原因    

    从语法上来讲,一个函数被声明为protected或者private,那么这个函数就不能从“外部”直接被调用了。 对于protected的函数,子类的“内部”的其他函数可以调用之。 而对于private的函数,只能被本类“内部”的其他函数说调用。

    语法上就是这么规定的,你肯定也知道的咯。 那么为什么有时候将构造函数或者析构函数声明为protected的或者private的?

    通常使用的场景如下: 1。如果你不想让外面的用户直接构造一个类(假设这个类的名字为A)的对象,而希望用户只能构造这个类A的子类,那你就可以将类A的构造函数/析构函数声明为protected,而将类A的子类的构造函数/析构函数声明为public。例如:

    [cpp] view plain copy #include <iostream>  using namespace std;  class A  {   protected:       A(){}  public:     void test(){         cout << "call A test()" << endl;     }   };    class B : public A  {      public: B(){}        };    int main(int argc, char** argv)  {      A a; // error      B b; // ok      b.test();  }  

     

    2. 如果将构造函数/析构函数声明为private,那只能这个类的“内部”的函数才能构造这个类的对象了。这里所说的“内部”不知道你是否能明白,下面举个例子吧。 class A { private: A(){ } ~A(){ }

    public: void Instance()//类A的内部的一个函数 { A a; } }; 上面的代码是能通过编译的。上面代码里的Instance函数就是类A的内部的一个函数。Instance函数体里就构建了一个A的对象。 但是,这个Instance函数还是不能够被外面调用的。为什么呢? 如果要调用Instance函数,必须有一个对象被构造出来。但是构造函数被声明为private的了。外部不能直接构造一个对象出来。 A aObj; // 编译通不过 aObj.Instance(); 但是,如果Instance是一个static静态函数的话,就可以不需要通过一个对象,而可以直接被调用。如下:

    [cpp] view plain copy #include <iostream>  using namespace std;    class A  {  private:      A():data(10){ cout << "A" << endl; }      ~A(){ cout << "~A" << endl; }    public:      static A& Instance()      {          static A a;          return a;      }      void Print()      {          cout << data << endl;      }    private:      int data;  };    int main(int argc, char** argv)  {      A& ra = A::Instance();      ra.Print();  }  

     

    上面的代码其实是设计模式singleton模式的一个简单的C++代码实现。

    还有一个情况是:通常将拷贝构造函数和operator=(赋值操作符重载)声明成private,但是没有实现体。 这个的目的是禁止一个类的外部用户对这个类的对象进行复制动作

                    

    总的来说单体模式。保证程序最多创建一个实例 static: 静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。 在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员。 如果静态成员函数中要引用非静态成员时,可通过对象来引用。下面通过例子来说明这一点。 #include  class M { public: M(int a) { A=a; B+=a;} static void f1(M m); private: int A; static int B; }; void M::f1(M m) { cout<<"A="<<m.a<<endl;  cout<<"B="<<b<<endl;  } int M::B=0; void main() { M P(5),Q(10); M::f1(P); file://调用时不用对象名 M::f1(Q); } 转载  :感谢 C++中的static关键字的总结

      C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static。前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用。

    1.面向过程设计中的static 1.1静态全局变量 在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量。我们先举一个静态全局变量的例子,如下: //Example 1 #include <iostream.h> void fn(); static int n; //定义静态全局变量 void main() {    n=20;    cout<<n<<endl;    fn(); }

    void fn() {    n++;    cout<<n<<endl; } 静态全局变量有以下特点: • 该变量在全局数据区分配内存; • 未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化); • 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的; 

    静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量。对于一个完整的程序,在内存中的分布情况如下图:  代码区 全局数据区 堆区 栈区 一般程序的由new产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。细心的读者可能会发现,Example 1中的代码中将 “static int n; //定义静态全局变量”改为“int n; //定义全局变量”。程序照样正常运行。的确,定义全局变量就可以实现变量在文件中的共享,但定义静态全局变量还有以下好处: • 静态全局变量不能被其它文件所用; • 其它文件中可以定义相同名字的变量,不会发生冲突;

    您可以将上述示例代码改为如下: //Example 2 //File1 #include <iostream.h> void fn(); static int n; //定义静态全局变量 void main() {    n=20;    cout<<n<<endl;    fn(); }

    //File2 #include <iostream.h> extern int n; void fn() {    n++;    cout<<n<<endl; } 编译并运行Example 2,您就会发现上述代码可以分别通过编译,但运行时出现错误。试着将 “static int n; //定义静态全局变量”改为“int n; //定义全局变量” 再次编译运行程序,细心体会“全局变量”和"静态全局变量"的区别。

    1.2.静态局部变量 在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。 我们先举一个静态局部变量的例子,如下: //Example 3 #include <iostream.h> void fn(); void main() {    fn();    fn();    fn(); } void fn() {    static n=10;    cout<<n<<endl;    n++; } 通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。但有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。 静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。 静态局部变量有以下特点: • 该变量在全局数据区分配内存; • 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化; • 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0; • 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;

    1.3静态函数   在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。 静态函数的例子: //Example 4 #include <iostream.h> static void fn();//声明静态函数 void main() {    fn(); } void fn()//定义静态函数 {    int n=10;    cout<<n<<endl; } 定义静态函数的好处: • 静态函数不能被其它文件所用; • 其它文件中可以定义相同名字的函数,不会发生冲突;

     

    二、面向对象的static关键字(类中的static关键字) 2.1静态数据成员 在类内数据成员的声明前加上关键字static,该数据成员就是类内的静态数据成员。先举一个静态数据成员的例子。 //Example 5 #include <iostream.h> class Myclass { public:    Myclass(int a,int b,int c);    void GetSum(); private:    int a,b,c;    static int Sum;//声明静态数据成员 }; int Myclass::Sum=0;//定义并初始化静态数据成员

    Myclass::Myclass(int a,int b,int c) {    this->a=a;    this->b=b;    this->c=c;    Sum+=a+b+c; }

    void Myclass::GetSum() {    cout<<"Sum="<<Sum<<endl; }

    void main() {    Myclass M(1,2,3);    M.GetSum();    Myclass N(4,5,6);    N.GetSum();    M.GetSum();

    } 可以看出,静态数据成员有以下特点: • 对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新; • 静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。在Example 5中,语句int Myclass::Sum=0;是定义静态数据成员; • 静态数据成员和普通数据成员一样遵从public,protected,private访问规则; • 因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它; • 静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为: <数据类型><类名>::<静态数据成员名>=<值> • 类的静态数据成员有两种访问形式: <类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名> 如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员 ; • 静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了; • 同全局变量相比,使用静态数据成员有两个优势: 1. 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性; 2. 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

    2.2静态成员函数   与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。静态成员函数与静态数据成员一样,都是类的内部实现,属于类定义的一部分。普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。通常情况下,this是缺省的。如函数fn()实际上是this->fn()。但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。下面举个静态成员函数的例子。 //Example 6 #include <iostream.h> class Myclass { public:    Myclass(int a,int b,int c);    static void GetSum();/声明静态成员函数 private:    int a,b,c;    static int Sum;//声明静态数据成员 }; int Myclass::Sum=0;//定义并初始化静态数据成员

    Myclass::Myclass(int a,int b,int c) {    this->a=a;    this->b=b;    this->c=c;    Sum+=a+b+c; //非静态成员函数可以访问静态数据成员 }

    void Myclass::GetSum() //静态成员函数的实现 {   // cout<<a<<endl; //错误代码,a是非静态数据成员    cout<<"Sum="<<Sum<<endl; }

    void main() {    Myclass M(1,2,3);    M.GetSum();    Myclass N(4,5,6);    N.GetSum();    Myclass::GetSum(); } 关于静态成员函数,可以总结为以下几点: • 出现在类体外的函数定义不能指定关键字static; • 静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数; • 非静态成员函数可以任意地访问静态成员函数和静态数据成员; • 静态成员函数不能访问非静态成员函数和非静态数据成员; • 由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长; • 调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以直接使用如下格式: <类名>::<静态成员函数名>(<参数表>) 调用类的静态成员函数。

    转载请注明原文地址: https://ju.6miu.com/read-801.html

    最新回复(0)