为什么使用模板:
C++重载(overloading),重载函数一般含有不同的参数个数或不同的参数类型。如果使用MAX函数对不同类型的求最大值,就要定义不同的重载函数:
int max(int x,int y) { if(x>y) return x; else return y; } float max(float x,float y) { if(x>y) return x; else return y; }
但是如果要用double型,又需要重新定义。。。
模板就是为了解决重载函数定义不全的问题,它可以实现类型参数化,即把类型定义为参数,从而实现代码的可重用性。模板分为两类:函数模板和类模板
函数模板:
Template<class 或 typename T>
template是一个声明的模板的关键字,表示声明一个模板关键字class不能省。
template <class T> T min(T x,T y) { return(x<y)?x:y; }
类模板定义:
template <class T>
class 类名{
};
template <class T1,class T2> class student{ T1 a; T2 b; public: student(T1 a,T2 b);//constructor void show(); }; template<class T1, class T2> student<T1,T2>::student(T1 a,T2 b):a(a),b(b){} template<class T1,class T2> void student<T1,T2>::show() { cout<<a<<b<<endl; } int main() { student<char ,char> s('a','b'); s.show(); return 0; }
把类的属性定义为private,保护数据,不能外部随意访问,但是可以定义一个访问内部数据的函数接口。也可以定义一个修改内部属性的函数(那这与外部访问数据和修改数据有什么区别,这样可以在修改函数里加各种限制。哈哈)
array < int > a(10);
array < int >表明用int类型来代替模板类中的类参数“T”,编译器会将模板类array中所有的类参数T都用int来代替。例如类中的私有成员变量“T * num;”会被替换为“int * num;”。对类中的成员函数也会进行相同的替换,如“T & operator[]( int );”将会被替换为“int & operator[]( int );”。
using namespace std; template< class T > class array { public: array( int ); T & operator[]( int );//操作符[]取第int个数,返回类型为T。。 const T & operator[] ( int )const; int getlen() const { return length; } ~array(); private: array(){}; int length; T * num; }; template < class T > array< T >::array( int n ) { num = new T[n]; length = n; } template < class T > array< T >::~array() { delete[] num; } template< class T > T & array< T > ::operator[] ( int i ) { if( i < 0 || i >= length ) throw string( "out of bounds" ); return num[i]; } template< class T > const T & array< T > ::operator[] (int i) const { if( i < 0 || i >= length) throw string( "out of bounds" ); return num[i]; } template< class T > ostream & operator<<( ostream & out, const array <T> & A ) { for(int i=0; i < A.getlen(); i++) out<< A[i] << " "; return out; } int main() { array< int > A(10); for(int i = 0; i < 10; i++) { A[i] = 2*i; } cout<<A<<endl; return 0; }
C++编程技巧:对象与实例的区别,new与不用new的区别:
class A a; a 在栈里;
class A a=new A; A 在堆里;
new创建类对象需要指针接收,一处初始化,多处使用; CTest* p= new CTest();//new申请的对象,则只有调用到delete时再会执行析构函数 ; 不用new CTest mTest;//不需要手动释放,该类析构函数会自动执行
new需要delete销毁
new创建对象在堆空间
频繁调用不适合用new,要申请和释放内存
用new 生成对象,上面的例子写为:
int main() { student<char ,char>* s=new student<char ,char >('a','b'); s->show(); delete(s); return 0; }
(java中不同:JAVA:
A a = new A();
为A对象创建了一个实例,但在内存中开辟了两块空间:一块空间在堆区,存放new A()这个对象;
另一块空间在堆栈,也就是栈,存放a,a的值为new A()这个对象的内存地址。因为java在JVM中运行,
所以a 描述的内存地址不一定是这个对象真实内存的地址。
Object o; // 这是声明一个引用,它的类型是Object,他的值为null,还没有指向任何对象,该引用放在内存的栈区域
o = new Object(); // new Object()句,实例化了一个对象,就是在堆中申请了一块连续空间用来存放该对象。
= // 运算符,将引向o指向了对象。也就是说将栈中表示引用o的内存地址的内容改写成了Object对象在堆中的地址。
创建对象:student stu1 = new student();
通常把这条语句的动作称之为创建一个对象,其实,它包含了四个动作。
1)右边的"new student",是以student类为模板,在堆空间里创建一个student类的对象(也简称为student对象)。
2)末尾的()意味着,在对象创建后,立即调用student类的构造函数,对刚生成的对象进行初始化。
构造函数是肯定有的。如果你没写,Java会给你补上一个默认的构造函数。
3)左边的"student stu1"创建了一个student类引用变量。所谓student类引用,就是以后可以用来指向某个
student对象的对象引用,它指向的是某个student对象的内存地址(有点C语言中指针的味道)。
4)"="操作符使对象引用指向刚创建的那个student对象。
我们可以把这条语句拆成两部分:student stu1; (1)
stu1 = new student(); (2)
效果是一样的。
)
C++ 如果直接定义类,如classA a; a 存在栈上(也意味着复制了对象a在栈中),如果classA a = new classA就存在堆
非类型模板参数:
C++中:
Student student(20) ; //这里student是引用 对象分配在 栈空间中,这里只是我的理解
Student *student = new Student(20); //这里student是指针,new Student(20)是分配在堆内存空间的
- Student stu;//对象 stu 在栈上分配内存,需要使用
&
获取它的地址 - Student *pStu = &stu;//创建对象指针
在堆上创建对象,这个时候就需要使用前面讲到的new
关键字
- Student *pStu = new Student;
在栈上创建出来的对象都有一个名字,比如 stu,使用指针指向它不是必须的。但是通过 new 创建出来的对象就不一样了,它在堆上分配内存,没有名字,只能得到一个指向它的指针,所以必须使用一个指针变量来接收这个指针,否则以后再也无法找到这个对象了,更没有办法使用它。也就是说,使用 new 在堆上创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。栈内存是程序自动管理的,不能使用 delete 删除在栈上创建的对象;堆内存由程序员管理,对象使用完毕后可以通过 delete 删除。在实际开发中,new 和 delete 往往成对出现,以保证及时删除不再使用的对象,防止无用内存堆积。
new A以及new A()的区别:
调用new分配的内存有时候会被初始化,而有时候不会,这依赖于A的类型是否是POD(Plain old data)类型,或者它是否是包含POD成员、使用编译器生成默认构造函数的类。POD class没有用户定义的析构函数、拷贝构造函数和非静态的非POD类型的数据成员。没有用户定义的构造函数,没有私有的或者保护的非静态数据,没有基类或虚函数。它只是一些字段值的集合,没有使用任何封装以及多态特性。
#include <iostream> using namespace std; struct A { int m; }; // POD struct B { ~B(){}; int m; }; // non-POD, compiler generated default ctor struct C { C() : m() {}; ~C(){}; int m; }; // non-POD, default-initialising m int main() { A *aObj1 = new A; A *aObj2 = new A(); cout << aObj1->m << endl; cout << aObj2->m << endl; B *bObj1 = new B; B *bObj2 = new B(); cout << bObj1->m << endl; cout << bObj2->m << endl; C *cObj1 = new C; C *cObj2 = new C(); cout << cObj1->m << endl; cout << cObj2->m << endl; delete aObj1; delete aObj2; delete bObj1; delete bObj2; delete cObj1; delete cObj2; return 0; }
new A:不确定的值
new A():value-initialize A,由于是POD类型所以是zero initialization
new B:默认构造(B::m未被初始化)
new B():value-initialize B,zero-initialize所有字段,因为使用的默认构造函数
new C:default-initialize C,调用默认构造函数
new C():value-initialize C,调用默认构造函数
在所有C++版本中,只有当A是POD类型的时候,new A和new A()才会有区别。而且,C++98和C++03会有区别。
但是在Java中
Student student(20) ; //注意:java中没有这样实例化对象的, 要想得到一个对象 必须要new出来.
Student student ; //这个只是定义了一个引用 ,没有指向任何对象
Student student = new Student(20); //定义了一个引用,指向堆内存中的student对象
常数,或指向外部链接对象的指针。
template <class T,MAXSIZE> class Stack{ T elemsp[MAXSIZE]; }; Stack<int, 20> intStack; Stack<int ,10> intStack2;