目录
1、关键字new:
1、用法:
2、理解:
3、与malloc的相同与不同:
1、相同:
2、不同:
2、模版初阶:
1、函数模版:
1、概念:
2、关键字:template:
3、模版原理:
4、函数模版的实例化:
1、隐式实例化:
2、显式实例化:
2、类模版:
1、定义格式:
2、类模版的实例化:
1、关键字new:
new是相对于C语言的malloc一个新的内存管理方式,通过new和delete配套使用来进行动态内存管理。
1、用法:
类型* 名字 = new 类型;
例如:
int* ptr1 = new int;
这就是动态申请了一个int类型空间。
2、理解:
1、new()
这样在()里面可以进行初始化,例如:
int* ptr2 = new int(10);这就是动态申请一个int类型的空间并初始化为10
2、new[n]
这样就是分配这种类型的n个大小的内存空间,并用默认构造函数来初始化这些变量;
例如:
int* ptr6 = new int[10];这样就是动态申请10个int类型的空间。
3、与malloc的相同与不同:
1、相同:
都是从对上申请的空间,并且都需要手动释放。
2、不同:
其余的还有:
1、malloc和free是函数,new和delete是操作符
2、malloc申请的空间不会初始化,new可以初始化(调用构造函数)
3、malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[]中指定对象个数即可
4、malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
5、malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
6、new/delete和malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数初始化和析构函数清理,但是二者对于内置类型几乎是一样的。
class A
{
public:A(int a = 0): _a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(1);free(p1);delete p2;return 0;
}
调用构造函数就可以对自定义类型进行初始化了,比如在链表那里,可以不用newnode了
2、模版初阶:
引入:
在C语言中交换函数Swap存在“写死”,但是一会儿要交换int类型,一会儿要交换double类型,那么就要我们写很多不同种类的函数,就很繁琐,在C++中就引入了模版的概念,这样可以大大减少代码的重复度。
1、函数模版:
1、概念:
函数模板代表了一个函数家族(比如Swap函数,可交换int double 等等),该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
2、关键字:template:
模版格式:
template<typename T1,typename T2,......,typename Tn>
void 函数名(T& 形参名1,......,T& 形参名n)//注意权限情况加const
以下是示例代码:
template<typename T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}
int main()
{int a = 1;int b = 2;Swap(a, b);cout << "a = " << a << endl << "b = " << b << endl;return 0;
}
3、模版原理:
虽然我们只写了一个模版函数,但是实际上调用是因为编译器帮我们通过函数模版实现了不同的函数,再调用它们来达到不同类型函数的调用。
4、函数模版的实例化:
函数模板的实例化:用不同类型的参数使用函数模板。
1、隐式实例化:
让编译器根据实参推演模板参数的实际类型叫做隐式实例化。
2、显式实例化:
在函数名后的<>中指定模板参数的实际类型。
template<typename T>
T Add(const T& x,const T& y)
{return x + y;
}int main()
{int a = 10;double b = 20.10;cout << Add(a, b) << endl;return 0;
}
上述代码中有个问题:就是a和b是不同类型的,但是在函数中又必须是同一种类型的,这样的话就可以用显式实例化或者在Add(a,b)中进行转换。
1、在Add(a,b)中转化就是转化为:Add((double)a,b)或者Add(a,(int)b)
2、更好的一种方法就是用显式实例化:
改为:Add<int>(a,b)或者Add<double>(a,b)
但是实际上不会这么用,在后面list等等数据结构中会用到。
2、类模版:
1、定义格式:
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
2、类模版的实例化:
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板的名字不是真正的类,就是个名字,而实例化的结果才是真正的类。
比如我写了一个栈模版
template<class T>
class Stack
{
public:Stack(size_t capacity = 3);void Push(const T& data);// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}private:T* _array;int _capacity;int _size;
};
那么栈的类名:Stack
栈的类型:Stack<T>
这里是将声明与定义分离了,在函数外面进行定义就需要加上模版的声明和类域的划分
template<class T>
Stack<T>::Stack(size_t capacity)
{_array = new T[capacity];_capacity = capacity;_size = 0;
}template<class T>
void Stack<T>::Push(const T& data)
{_array[_size] = data;_size++;
}