目录
一、static关键字(C语言)
1、static 修饰的变量就是静态数据 。
2、静态数据只能被初始化一次 。
3、静态数据存储在数据段。
4、静态全局变量,只能在当前文件中使用。
二、类中的静态成员特点
1、类的静态数据成员,不包含在类的空间中。 (静态数据:存储在数据段)。
2、类的静态数据成员,无法在类内初始化! (只能在类外初始化)。
int base::data = 100; //类外初始化类中的静态成员 。
3、类的权限还是可以影响静态数据成员的(假设该数据成员是公有则可以不用定义对象访问)。
cout << base::data << endl; //直接通过类名访问。
4、类中的静态数据成员是所有该类的对象共享的。
#include <iostream>
using namespace std;
class base {
public:
base(){}
void set_data(){
data = 123456;
}
public:
static int data; //静态数据成员
};
int base::data = 100; //类外初始化类中的静态成员
int main()
{
base a;
a.data = 10086;
base b;
cout << a.data << endl;
cout << b.data << endl;
b.data = 10010;
cout << a.data << endl;
cout << b.data << endl;
a.set_data();
cout << a.data << endl;
cout << b.data << endl;
}
三、类中的静态成员函数特点
1、类中的静态成员函数无法使用 this 指针 。
2、类中的静态成员函数无法使用 非静态数据。 (因为静态成员函数是先于类存在的)
3、类中的静态成员函数可以直接调用。 (不需要创建对象)
#include <iostream>
using namespace std;
int value=10086;
class base {
public:
//静态成员函数
static void set_data(int d){
// base::data = d; 无法访问非静态数据
value = d; //因为value 存储在数据段 ,是先于 main 函数存在的
my_data = d;//类中的静态数据成员
}
//普通函数
void show_data(){
cout << this->data << endl;
cout << this->my_data << endl;
}
private:
int data;
static int my_data;
};
int base::my_data=0;//类外初始化,类中的静态成员
int main()
{
/*
base a;
a.set_data(10086);
a.show_data();
*/
base::set_data(10010); //不需要创建对象,直接调用。 简化调用,节省空间
base::show_data(); //必须创建对象后调用
}
四、类中const成员
const : 修改变量的属性为常量,常量是无法修改的!
特点:
1、类中的常量数据成员,必须要初始化,否则无法定义对象。
(1)构造函数参数列表中初始化 base(int tmp):data(tmp)
(2)定义常量数据成员的时候初始化) const int data=100;
2、初始化后就无法修改 ,且属于类中的数据成员 。
#include <iostream>
using namespace std;
class base
{
public:
base(int tmp) : data(tmp){ }
void set_base(){
// this->data = 100; //常量数据成员无法修改 ?
}
void show(){
cout << this->data << endl;
}
private:
const int data = 100;
};
int main()
{
base tmp(100);
tmp.show();
cout << sizeof(base) << endl;
}
五、类中的const 成员函数特点
1、定义常量成员函数必须在后面添加 const 。
void show() const {}
2、常量成员函数,无法修改类中的数据成员。 (只能访问,无法修改)
3、如果想要修改类中的数据成员,必须添加 mutable 修饰数据成员。
#include <iostream>
using namespace std;
int value = 10086;
class base
{
public:
//静态成员函数
static void set_data(int d)
{
// base::data = d; 无法访问非静态数据
value = d; //因为value 存储在数据段 ,是先于 main 函数存在的
my_data = d; //类中的静态数据成员
}
//普通函数
void show_data()
{
cout << this->data << endl;
cout << this->my_data << endl;
}
private:
int data;
static int my_data;
};
//类外初始化,类中的静态成员
int base::my_data = 0;
int main()
{
/*
base a;
a.set_data(10086);
a.show_data();
*/
base::set_data(10010); //不需要创建对象,直接调用。 简化调用,节省空间
base::show_data(); //必须创建对象后调用
}
六、类中的const 对象特点
1、常量对象,无法修改类中的数据成员
const base tmp;
tmp.data = 10010; //错误的,无法修改。
2·常量对象,无法调用类中的非常量成员函数。 //常量对象的目的就是为了不要修改,类中的数据成员,假设调用非常 量成员函数,还是可以修改这就失去常量对象的作用了。
3、mutable 可以修饰变量,可被修改!
#include <iostream>
using namespace std;
class base
{
public:
void set_base(int d) const{
this->data = d;
}
//常量成员函数
void show() const{
cout << this->data << endl;
}
//修饰为可以修改
mutable int data = 100;
};
int main()
{
const base tmp; //定义一个常量对象
tmp.set_base(123); //利用接口修改数据
// tmp.show();
tmp.data = 10010; //直接修改数据
tmp.show();
}
七、模板
1、为什么要模板?
答:提高代码的复用性!
例子:
例如:设计一个函数实现两个数据的相加
C语言: 必须要设计多个函数接口
char add_char(char a,char b);
float add_float(float a,float b);
char * add_str(char *a,char *b);
C++: 利用函数重载
char add(char a,char b);
float add(float a,float b);
char * add(char * a,char * b);
C++ 只是优化了不需要,编写多个不同函数名和调用时,不需要分类型调用。但是还是要写多个函数!
C++ 模板: 函数模板
通用类型 T
T add(T a,T b); 实现两个数,相加的模板 , T 是一种通用类型,表示任意数据类型。
//节省代码量
2、通用类型的定义
template<typename 通用类名称> 或 template<class 通用类名称>
当一个函数中包含一个通用类型,那么这个函数就是模板函数。
3、模板函数的定义
template<class 通用类名称>
通用类名称 函数名(通用类名称 变量名){
函数主体
}
定义一个模板输出不同的数据类型
template<class T>
T print_func(T value){
cout << value;
}
例子:
#include <iostream>
using namespace std;
定义一个通用类型T
template <typename T>
T print_func(T value){
cout << value << endl;
}
int main()
{
print_func("hello"); //根据传递的数据类型自动转换为
//const char *print_func<const char *>(const char *value)
print_func(3.14); //自动转换为: double print_func<double>(double value)
print_func('S'); //自动转换为: char print_func<char>(char value)
print_func(1024);//自动转换为: int print_func<int>(int value)
}
模板函数的特点:
1、模板函数会发生自动类型推导,根据用户传递的参数类型确定 T 通用类型。
2、一个通用类型只能应用一个函数。
template<class R>
R swap_data(R &a,R &b)
//R pf(R value); 错误的,R 使用在上面的函数使用
3、一个通用类型推导一种数据类型。
swap_data("hello",100); 错误的,一个通用类型R ,无法推导出两种类型
八、模板函数与普通函数区别
1、如果模板函数与普通函数实现的功能一致,优先使用普通函数。
2、如果模板函数的参数匹配度比普通函数高,优先使用模板函数。
3、普通函数可以发生自动类型转换, 模板函数只能发生自动类推导。
4、假设想要系统调用模板函数,可以声明多几个通用类型 或 者指定调用模板。
(1)声明多个通用类型
template <class T,class T1>
void pf_func(T a,T1 b)
(2)指定使用模板
//指定使用模板函数,并指定模板类型
pf_func<int>(3.14,20);
5、模板函数也可以重载
例子:
#include <iostream>
using namespace std;
普通函数
void pf_func(int a,int b){
cout << "普通函数" << endl;
输出类型名称
cout << typeid(a).name() << endl;
cout << a << endl;
cout << b << endl;
}
模板函数
template <class T>
void pf_func(T a,T b){
cout << "模板函数" << endl;
cout << typeid(a).name() << endl;
cout << a << endl;
cout << b << endl;
}
模板函数的重载
template <class T>
void pf_func(T a){
cout << "模板函数" << endl;
cout << typeid(a).name() << endl;
cout << a << endl;
}
int main()
{
//指定使用模板函数,并指定模板类型
pf_func<int>(3.14,20);
pf_func(123);
}
九、模板函数的局限性
问题: 对于用户自定义的数据类型,模板函数只能推导,不能操作。
解决方法:
1、重载用户自定义的数据的操作运算符 。
2、编写一个普通函数处理用户自定义数据。
例子:
#include <iostream>
using namespace std;
//自定义一个类型
class base {
public:
base(int d):data(d){}
void show(){
cout << data << endl;
}
private:
int data;
//friend bool operator>(base a,base b);
friend base big_data(base value,base value1);
};
#if 0
重载比较运算符
bool operator>(base a,base b)
{
if(a.data > b.data )
{
return true;
}else
{
return false;
}
}
#endif
定义一个通用类型T
template <typename T>
T big_data(T value,T value1)
{
if(value > value1)
{
return value;
}else{
return value1;
}
}
重载函数实现 base 的比较
base big_data(base value,base value1)
{
if(value.data > value1.data)
{
return value;
}else{
return value1;
}
}
int main()
{
base tmp(10);
base tmp1(20);
base max = big_data(tmp,tmp1); //base big_data<base>(base value, base value1)
max.show();
}
十、模板类
1、当一个类包含一个通用类型时,这个类就是模板类。
//定义一个通用类型
template <class T>
//利用通用类型定义模板类
class base {
private:
T data;
};
模板类:必须实例化才能使用 。
base tmp; //错误的,没有实例化模板,无法定义对象
base<int> tmp1; //模板实例化
模板类作为参数
当一个类是模板类,就无法直接通过类名作为参数传递 。
解决方法:
1、在参数列表中,指定模板类的类型。
void show_base(base<int> tmp)
2、继续把类型模板化
template <class T>
void show_base1(base<T> tmp)
3、直接把参数设为模板类型
template <class T>
void show_base2(T tmp)
例子:
#include <iostream>
using namespace std;
定义一个通用类型
template <class T>
利用通用类型定义模板类
class base {
public:
base(T value):data(value){}
void show(){
cout << data << endl;
}
T data;
};
1.指导模板类的类型
void show_base(base<int> tmp) 如果数据是私有成员,可以用友元访问
{
cout << tmp.data << endl;
}
2.继续把类型模板化
template <class T>
void show_base1(base<T> tmp) 如果数据是私有成员,不能友元来访问
{
cout << tmp.data << endl;
}
3.把参数设置为模板类型
template <class T>
void show_base2(T tmp) 如果数据是私有成员,不能友元来访问
{
cout << tmp.data << endl;
}
int main()
{
base<int> tmp(10086);
show_base(tmp);
show_base1(tmp);
show_base2(tmp);
}
十一、模板类的成员函数类外编写
#include <iostream>
using namespace std;
template <class T>
class base
{
public:
void set_base(T value);
void show_base();
private:
T data;
};
类外编写base 模板类的成员函数
template <class T>
void base<T>::set_base(T value){
this->data = value;
}
template <class T>
void base<T>::show_base(){
cout << this->data << endl;
}
int main()
{
定义一个模板类对象
base<int> tmp;
tmp.set_base(10086);
tmp.show_base();
}
模板类的成员函数,最好在类内编写,比较简单。
十二、模板类的继承
问题:模板类是无法直接派生
解决方法:
1、在派生时指定模板类的类型
class base_a : public base<int>
2、继续把派生类设置为模板类
template <class T>
class base_b:public base<T>
例子:
#include <iostream>
using namespace std;
定一个模板类
template <class T>
class base {
public:
base(T v):data(v){}
void show(){cout << data << endl;}
private:
T data;
};
//派生类 -> 已经实例化了,派生类不再是模板类
class base_a : public base<int> {
public:
base_a(int value):base(value){}
};
继续把派生类设置为模板类
template <class T>
class base_b:public base<T>{
public:
base_b(T value):base<T>(value){}
};
int main()
{
base_a tmp(100);
tmp.show();
base_b<int> tmp1(200);
tmp1.show();
}
总结:
知晓模板的本质是为了更好的理解STL库,因为STL库是一个个封装好的模板,只需调用即可。