制作网站并销售狗类商品是否能够带来盈利?软件设计流程图如何优化以提升效率?
摘要:做网站卖狗挣钱吗,软件设计流程图,网站开发诺亚科技,东莞市专注网站建设平台C另一种编程思想称为 泛型编程,主要利用的技术就是模板 目录 C另一种编程思想称为 泛型编程,主要利用的技
做网站卖狗挣钱吗,软件设计流程图,网站开发诺亚科技,东莞市专注网站建设平台C另一种编程思想称为 泛型编程#xff0c;主要利用的技术就是模板 目录 C另一种编程思想称为 泛型编程#xff0c;主要利用的技术就是模板 一、概念 二、函数模板 1、语法与使用#xff1a; 2、函数模板注意事项 3、普通函数与函数模板的区别 4、普通函数与函数模板的调用规… C另一种编程思想称为 泛型编程主要利用的技术就是模板 目录 C另一种编程思想称为 泛型编程主要利用的技术就是模板 一、概念 二、函数模板 1、语法与使用 2、函数模板注意事项 3、普通函数与函数模板的区别 4、普通函数与函数模板的调用规则 5、模板的局限性 三、类模板
作用建立一个通用类类中的成员属性的数据类型可以不具体指定用一个虚拟的类型来表示
1、语法与使用 2、类模板与函数模板的区别 3、类模板中成员函数创建时机
4、类模板对象作函数参数
三种方式
①指定传入的类型 直接显示对象的数据类型主要用的类型
②参数模板化 将对象中的参数变为模板进行传递
③整个类 模板化 将这个对象类型 模板化进行传递 5、类模板与继承
①当子类继承的父类是一个类模板时子类在声明时要指定出父类中T的类型
②如果不指定编译器无法给子类分配内存因为不知道父类中成员的大小
③如果想灵活指出父类中T的类型子类也需要变成类模板
6、类模板成员函数类外实现
①类模板构造函数类外实现
②类模板成员函数类外实现
7、类模板分文件编写
①直接包含.cpp文件
②将声明与实现写在一个hpp文件中约定俗成hpp并不必须
8、类模板与友元
①全局函数类内实现直接在类内声明友元即可
②全局函数类外实现先提取让编译器知道全局函数的存在 9、类模板案例 实现一个通用的数组类要求
①可以对内置数据类型以及自定义数据类型进行存储
②将数组中的数据存储到堆区
③构造函数中要传入数组的容量
④提供拷贝构造函数以及operator以防止浅拷贝问题
⑤提供尾插法与尾删法对数组中数据进行增加与删除
⑥通过下标的方式访问数组中的元素
⑦获取当前数组中元素的个数以及数组的容量 一、概念
建立通用的模具提高复用性
如拍一寸照片的模板 制作PPT的模板 但是注意模板并不是万能的如人的1寸照片模板不能用来拍其他生物的照片
而且制作PPT时不能直接使用需要自己加内容啥的 因此
①模板不能直接使用只是一个框架需要自己合理使用
②模板是通用的但不是万能的 二、函数模板
作用建立一个通用函数其返回值类型与形参类型可以不指定用一个虚拟的类型来代表 1、语法与使用
templatetypename T
函数声明或定义template声明创建模板
typename表示其后面的符号是一种数据类型可以用class
T通用的数据类型名称可以替换通常为大写字母T 接下来使用交换函数swap来举例
首先写出int整型交换函数与float浮点型交换函数
// 整型 交换函数
void swapInt(int x, int y)
{int tmp y;y x;x tmp;
}
// 浮点型 交换函数
void swapFloat(float x, float y)
{float tmp y;y x;x tmp;
}
测试
int main()
{int a 10;int b 20;swapInt(a, b);cout a b endl;float c 10.5;float d 20.5;swapFloat(c, d);cout c d endl;return 0;
} 可以正常交换并输出 但是想要交换不同的数据就必须实现不同的函数改变返回值类型或者形参类型但是具体代码又类似
因此我们使用函数模板
templatetypename T
void Myswap(T x, T y)
{T tmp y;y x;x tmp;
}
调用Myswap函数 int a 10;int b 20;Myswap(a, b);cout a b endl;float c 10.5;float d 20.5;Myswap(c, d);cout c d endl; 而我们在调用函数时并未告知Myswap函数我们传入的类型是什么使编译器自动推导出类型并实现
这叫做 自动推导类型 还有个 显式指定类型 int e 100;int f 20;Myswapint(e, f);cout e f endl; 就是调用函数时在参数列表前加上int这就相当于告知编译器刚才的类型T等于int 2、函数模板注意事项
①自动推导类型必须推导出一致的数据类型T才可以正常使用
以我们上面的交换函数Myswap举例
templatetypename T
void Myswap1(T x)
{T tmp y;y x;x tmp;
}
我们写2个不同的数据类型尝试调用 int a 10;float b 22.4;Myswap1(a, b);
直接报错 因此不能使用
不过如果在函数模板里写2个typename则可以正常调用
templatetypename T,typename Y
void Myswap1(T x, Y y)
{Y tmp y;y x;x tmp;
} 2个数据类型 int a 10;float b 22.4;Myswap1(a, b); 此时可以正常输出但是输出结果都是int整型 ②模板必须要确定T的数据类型才可以使用
templatetypename T
void test01()
{cout ko endl;
}
此时函数内未声明T的数据类型
尝试调用 直接报错
因为未确定T的函数类型 解决
在函数调用参数列表前加上int即随便指定个数据类型因为函数中没有使用具体的类型 实例1
使用函数模板实现选择排序对不同数据类型的数组进行降序排序
首先使用模板实现选择排序
templatetypename T
void Select_sort(T arr[],int len)
{int index 0;for (int i 0; i len - 1; i){index i;for (int j i 1; j len; j){ // 我们认定的最大值比数组中某个值小if (arr[index] arr[j]){ // 交换两者下标index j;}}if (index ! i) // 如果交换了下标不等于原来的i{MYswap(arr[index], arr[i]);//进行数组中数据的交换}}
}
最后一步有数据的交换我们接着使用模板实现交换函数
templatetypename T
void MYswap(T x, T y)
{T tmp x;x y;y tmp;
}
这里就完成了不过为了使数组中内容可以呈现在屏幕上我再额外实现一个打印函数
templatetypename T
void print(T arr, int len)
{for (int i 0; i len; i){cout arr[i] ;}cout endl;
}
①int类型数组测试
void int_test()
{// 整型数组测试int arr[] { 2,4,6,1,3,26,7,1,2,4,9,0,2,3 };int len sizeof(arr) / sizeof(arr[0]);print(arr, len);Select_sort(arr, len);cout endl;print(arr, len);
}
②char类型数组测试
void char_test()
{// 字符数组测试char arr2[] zcnasijda;int len2 sizeof(arr2) / sizeof(char);print(arr2, len2);Select_sort(arr2, len2);cout endl;print(arr2, len2);
} 成功进行降序排序 3、普通函数与函数模板的区别 ①普通函数调用时可以发生自动类型转换隐式类型转换 ②函数模板调用时如果使用自动类型推导则不会发生隐式类型转换 ③函数模板调用时如果使用显式指定类型则可以发生隐式类型转换 首先创建普通函数
// 普通函数
void add01(int x, int y)
{cout x y endl;
}
调用 add01(10, 20); 创建字符变量c并调用 char c c;add01(10, c); 因为c的ASCII码值是999910109函数在内部进行隐式类型转换
下面用函数模板实现
templatetypename T
void add02(T x, T y)
{cout x y endl;
}使用自动类型推导调用10与字符c 直接报错因为不能进行隐式类型转换
而我们使用显式指定类型 可以正常输出因为进行了隐式类型转换无论你传入什么数据都给你转成int转不成就报错 4、普通函数与函数模板的调用规则
①如果普通函数与函数模板都可以实现优先调用普通函数
实现一个大致相同的普通函数与函数模板
void print(int x, int y)
{cout 普通函数 endl;
}
templatetypename T
void print(T x, T y)
{cout 函数模板 endl;
}
调用生成 ② 在情况①下通过空模板参数列表可以强制调用函数模板 即在函数调用的参数列表前加
③函数模板也可以发送重载
templatetypename T
void print(T x, T y)
{cout 函数模板 endl;
}
templatetypename T
void print(T x, T y,T z) // 重载
{cout 函数模板 endl;
} ④如果函数模板可以发生更好的匹配优先调用函数模板 2个char类型的变量传入普通函数需要进行隐式类型转换而传入函数模板只需要进行自动类型推导因此优先调用函数模板 5、模板的局限性
局限性模板的通用并不万能
举例写一个判断变量是否相等的模板以及相等输出相等否则输出不等的函数
templatetypename T
bool My_compare(T a, T b)
{if (a b){return true;}else{return false;}
}
void judge(bool a)
{if (a){cout 相等 endl;}else{cout 不等 endl;}
}
测试
void test01()
{int a 10;int b 10;bool ret My_compare(a, b);judge(ret);
}
很明显ab 但是如果a和b的类型是一个类class
class person
{
public:person(string name,int age){m_age age;m_name name;}int m_age;string m_name;
};void test02()
{person p1(Tim, 22);person p2(Tim, 22);bool ret My_compare(p1, p2);judge(ret);
}
很明显p1p2但是编译器无法正常运行 因为person是自定义数据类型编译器不知道咋办 因此我们使用具体化person的版本实现代码具体化优先调用
在下面重写一个模板前面加template后面数据类型写person
templatebool My_compare(person p1, person p2)
{if (p1.m_age p2.m_age p1.m_name p2.m_name){return true;}else{return false;}
}
再次尝试运行
void test02()
{person p1(Tim, 22);person p2(Tim, 22);bool ret My_compare(p1, p2);judge(ret);
} 成功而传入数组判断相等也是同样的问题 总结 ①利用具体化的模板可以解决自定义类型的通用化 ②学习模板不是为了写模板而是为了在STL能够运用系统提供的模板 三、类模板
作用建立一个通用类类中的成员属性的数据类型可以不具体指定用一个虚拟的类型来表示 1、语法与使用
templateclass T
类
与函数模板基本相同只要把typename改成class就行而且二者可以互换效果相同 例写一个类模板其中有属性name与age不指定类型调用输出
templateclass Name_type,class Age_type
class person
{
public:person(Name_type name, Age_type age){m_name name;m_age age;}void show(){cout this-m_name this-m_age endl;}Name_type m_name;Age_type m_age;
};
测试
void test01()
{personstring, int p1(Joyce, 22);p1.show();
} 2、类模板与函数模板的区别
①类模板没有自动类型推导使用方式
对于刚才的类模板
templateclass Name_type,class Age_type
class person
{
public:person(Name_type name, Age_type age){m_name name;m_age age;}void show(){cout this-m_name this-m_age endl;}Name_type m_name;Age_type m_age;
};
如果我们使用自动类型推导 直接报错 而只能用显式指定类型
②类模板在模板参数列表中可以有默认参数
我们在模板的参数列表中加入默认参数 让Age_type直接等于int
void test02()
{personstring p2(tatina, 22); // 显式指定类型p2.show();
} 在调用时我们不用写其类型也可以正常运行 3、类模板中成员函数创建时机
普通类中成员函数一开始就可创建
类模板中成员函数在调用时才创建 例
首先创建2个类区分为类1与类2内部分别创建函数输出数字1与2
class person1
{
public:void show1(){cout 1 endl;}
};
class person2
{
public:void show2(){cout 2 endl;}
};
下面实现类模板
templateclass T
class Myclass
{
public:T obj;void m_show1(){obj.show1();}void m_show2(){obj.show2();}
};
测试参数列表传person1调用m_show1函数
void test02()
{Myclassperson1 m;m.m_show1();
} 输出1 尝试调用2 报错 此时就已经说明类模板中成员函数一开始没有创建只有在调用了才能确定对象的类型才能创建成员函数 而我们类型传入person2运行结果相反 4、类模板对象作函数参数
三种方式
①指定传入的类型 直接显示对象的数据类型日常主要用的类型
②参数模板化 将对象中的参数变为模板进行传递
③整个类 模板化 将这个对象类型 模板化进行传递 创建一个类模板参数类型为T1与T2
templateclass T1,class T2
class person
{
public:person(T1 name, T2 age){m_name name;m_age age;}void showinfo(){cout this-m_name this-m_age endl;}T1 m_name;T2 m_age;
};
①指定传入的类型
void print1(personstring, int p)// 直接把下面的类型拿来使用
{p.showinfo();
}
void test01()
{personstring, int p1(joyce, 21);print1(p1);
}
可见print1函数参数直接拿p1的数据类型使用这就是指定传入的类型 ②参数模板化
void test02()
{personstring, int p2(tatina, 20);print2(p2);
}
print2函数
templateclass T1, class T2
void print2(personT1, T2 p2)// 参树模板化为T1与T2
{p2.showinfo();cout typeid(T1).name() endl;// 查看T1的类型cout typeid(T2).name() endl;// 查看T2的类型
}
就是将参数也模板化为T1与T2
同时如果想查看数据的类型可以使用typeid().name()函数 ③整个类模板化 同样的测试函数
void test03()
{personstring, int p3(yomi, 1);print3(p3);
}
然后是print3将整个类模板化
templateclass T
void print3(T p)// 直接用T让编译器推导出类型
{p.showinfo();
}
看一下T的类型 5、类模板与继承
注意
①当子类继承的父类是一个类模板时子类在声明时要指定出父类中T的类型
②如果不指定编译器无法给子类分配内存因为不知道父类中成员的大小
③如果想灵活指出父类中T的类型子类也需要变成类模板 首先创建父类base类模板
templateclass T
class base
{
public:T m_a;
}; ①尝试创建子类
class son :public base
{;
};
直接报错 需要指定父类中T的类型 这样就可以
不过此时父类中的T只能是指定的T类型为了解决这个问题
②将子类也类模板
templateclass T1,class T2
class son2 :public baseT2
{T1 m_a;
};
void test02()
{son2string,int s2; // 显式指定类型
}
string就是T1int就是T2
而T2就是继承父类base并指定父类中T的类型为int 接着我们输出T1与T2的类型 6、类模板成员函数类外实现
掌握类模板中的成员函数类外实现 首先我们写一个常规的person类模板其中有属性m_name与m_age类型分别为T1与T2
带有构造函数与void show函数
templateclass T1,class T2
class person
{
public:person(T1 name,T2 age){this-m_name name;this-m_age age;}void show(){cout this-m_name this-m_age endl;}T1 m_name;T2 m_age;
};
①类模板构造函数类外实现
接下来我们先将类模板中的构造函数的实现部分屏蔽只留下声明在类模板外实现构造函数
templateclass T1,class T2
personT1, T2::person(T1 name, T2 age)
{this-m_name name;this-m_age age;
}
即在构造函数person(T1 name, T2 age)前加上作用域person::以及参数列表T1,T2
②类模板成员函数类外实现
templateclass T1,class T2
void personT1,T2::show() // 虽未用到T1T2但由于其是类模板中成员函数因此仍要写入T1/T2
{cout this-m_name this-m_age endl;
}
一样的加作用域与参数列表 测试
void test01()
{personstring, int p(joyce,21);p.show();
} 7、类模板分文件编写
首先分文件编写类模板时类模板.h头文件其中的成员函数在.cpp源文件实现成员函数创建时机是在调用阶段导致编写时链接不到 先创建person.h头文件其中写入person类模板
templateclass T1, class T2
class person
{
public:person(T1 name, T2 age);void show();T1 m_name;T2 m_age;
};
然后创建person.cpp源文件其中实现person的函数并包含.h头文件
// 构造函数
templateclass T1, class T2
personT1, T2::person(T1 name, T2 age)
{this-m_name name;this-m_age age;
}
// 成员函数
templateclass T1, class T2
void personT1, T2::show()
{cout this-m_name this-m_age endl;
}
到目前为止代码运行没有任何问题 但是我们在测试函数中调用构造函数与成员函数
#includeperson.hvoid test01()
{personstring, int p(joyce,21);p.show();
} 直接报错 因为编译器无法链接类模板成员函数的实现cpp文件 ①直接包含.cpp文件 我们将源文件包含的person.h文件改为person.cpp文件这样编译器直接链接到了cpp文件与h文件 成功实现 ②将声明与实现写在一个hpp文件中约定俗成名为hpp并非必须 上面是声明下面是类外实现 在源文件中包含 成功运行 8、类模板与友元
2种实现
①全局函数类内实现直接在类内声明友元即可
②全局函数类外实现先提前让编译器知道全局函数的存在 首先写一个person类模板其中有属性m_age与m_name以及构造函数person
templateclass T1, class T2
class person
{
public:person(T1 name, T2 age){this-m_name name;this-m_age age;}private:T1 m_name;T2 m_age;
};
①全局函数类内实现
接下来在其中加上友元的全局函数 // 全局函数 类内实现friend void print1(person T1, T2p){cout p.m_name p.m_age endl;}
测试
void test01()
{personstring,int p(joyce, 21);print1(p);
} ②全局函数类外实现
首先在类内写上声明 然后在类外实现
templateclass T1,class T2
void print2(person T1, T2p) // 因为有T1/T2因此需要加上类模板
{cout p.m_name p.m_age endl;
}
测试
void test02()
{personstring, int p(joyce, 21);print2(p);
} 依然是链接错误因为类模板内我们写的是普通函数的声明而下面实现写的是函数模板的实现二者毫不相干 解决①我们在声明的函数名后加上空参数列表
此时还未完成仍然无法使用紧接着
②先将类外实现部分移动到类模板的最上方 ③然后在类外实现部分的上面加上类模板的声明 这样下来才能正确运行 9、类模板案例 实现一个通用的数组类要求
①可以对内置数据类型以及自定义数据类型进行存储
②将数组中的数据存储到堆区
③构造函数中要传入数组的容量
④提供拷贝构造函数以及operator以防止浅拷贝问题
⑤提供尾插法与尾删法对数组中数据进行增加与删除
⑥通过下标的方式访问数组中的元素
⑦获取当前数组中元素的个数以及数组的容量 首先创建my_array类模板包含属性p_array存储开辟的数组m_capacity数组容量m_size数组当前大小
templateclass T
class my_array
{
public:// 防止浅拷贝问题的拷贝构造函数与operatormy_array(int capacity);// 有参构造函数my_array(const my_array p);// 拷贝构造函数my_array operator(const my_array p); // 复制运算符的重载,返回引用才是返回自身void push_back();// 尾插void pop_back(); // 尾删int get_capacity();// 获取容量int get_size(); // 获取大小~my_array();// 析构函数private:T* p_array; // 存储开辟的数组int m_capacity;// 数组的容量int m_size; // 数组当前大小
};
然后开始实现各个函数
①有参构造函数 my_array(int capacity)// 有参构造函数{this-m_capacity capacity;this-m_size 0;this-p_array new T[this-m_capacity]; // 开辟堆区空间以存储p_arraycout 有参构造函数 endl;}
②拷贝构造函数 my_array(const my_array p)// 拷贝构造函数{this-m_capacity p.m_capacity; this-m_size p.m_size;//this-p_array p.p_array;// 带来浅拷贝问题this-p_array new T(p.m_capacity);// 根据p的大小重新开辟空间以赋值// 将p中的数据也拷贝过来for (int i 0; i p.m_size; i){this-p_array[i] p.p_array[i];}cout 拷贝构造函数 endl;}
③operetor等号重载 my_array operator(const my_array p) // 赋值运算符的重载,返回引用才是返回自身{if (this-p_array ! NULL) // 判断是否有属性在堆区有则释放{delete[] this-p_array;this-p_array NULL;this-m_capacity 0;this-m_size 0;}// 深拷贝this-m_capacity p.m_capacity;this-m_size p.m_size;this-p_array new T[p.m_capacity]; // 重新开辟空间for (int i 0; i p.m_size; i) // 数据拷贝{this-p_array[i] p.p_array[i];}cout operator endl;return *this;}
④析构函数 ~my_array()// 析构函数{if (this-p_array ! NULL){delete[] this-p_array;this-p_array NULL;}cout 析构函数 endl;}
此时我们先运行一下各个函数
#includemy_array.hpp
int main()
{my_arrayint arr1(5); // 有参构造my_arrayint arr2(arr1);// 拷贝构造my_arrayint arr3(100); arr1 arr3; // 自定义类型赋值
return0;
} ⑤尾插 数据 首先判断数组当前元素个数size是否等于容量capacity如果相等则数组满了直接return。 否则直接将传入的数据赋给数组第size个元素arr[size]因为当前个数为size个数组下标为0-size-1的元素都有了下一个为空的就是第size个因此赋给第size个 void push_back(const T val)// 尾插{// 先判断数组是否满if (this-m_size this-m_capacity){return;/* int new_capacity m_capacity 1;this-p_array new T(new_capacity);*/}this-p_array[this-m_size ] val; // 尾插数据this-m_size; // 当前大小1}
⑥尾删 数据
使用户访问不到最后一个元素即可即 使size-1这样数组元素就少了一个 void pop_back() // 尾删{if (this-m_size 0){cout 数组为空 endl;return;}this-m_size--;}
⑦以下标方式访问数组元素
由于my_Array是我们自己创建的数组类型其中的元素数据类型都是T因此我们不能直接用[]访问到元素需要我们重载[]运算符 我们使用[]下标访问元素是为了获取一个数据所以我们重载就直接返回一个元素的数据就行 T operator[](int index){return this-p_array[index];}
返回值类型就是我们数组my_array中的数据类型T同时保证返回前后是同一个元素我们用 ⑧获取大小与获取容量
直接返回size与capacity即可 int get_capacity() // 获取容量{return this-m_capacity;}int get_size() // 获取大小{return this-m_size;}代码完成接下来
①我们创建数组并打印输出
templateclass T
void print(Tarr) // 打印函数
{for (int i 0; i 5; i){cout arr[i] ;}
}
int main()
{my_arrayint arr1(5); // 有参构造for (int i 0; i 5; i){arr1.push_back(i);}print(arr1);return 0;
} 成功输出范围内的数据
② 我们再看一下我们创建数组的容量和大小 cout capacity arr1.get_capacity() endl;cout size arr1.get_size() endl; ③我们拷贝arr1构造arr2并尾删数据再打印输出
void test01()
{my_arrayint arr1(5); // 有参构造for (int i 0; i 5; i){arr1.push_back(i);}print(arr1);cout capacity arr1.get_capacity() endl;cout size arr1.get_size() endl;my_arrayint arr2(arr1);// 拷贝构造print(arr2);arr2.pop_back();print(arr2);cout capacity arr2.get_capacity() endl;cout size arr2.get_size() endl;
} ④我们创建自定义数据类型然后使用尾插尾删等的函数
先创建自定义数据类型person
class person
{
public:person() {};person( string name,int age ){this-m_age age;this-m_name name;}int m_age;string m_name;
};
创建打印函数print2以我们自定义数据类型为准来创建
void print2(my_arrayperson arr)
{for (int i 0; i arr.get_size(); i){cout arr[i].m_name arr[i].m_ageendl;}cout endl;
}
创建数组arr并初始化先输出每个元素的信息与大小容量
然后尾删2个元素后再次输出每个元素的信息与大小容量
void test02()
{// 创建数组并初始化my_arraypersonarr(10);person a1(joyce, 21);person a2(tatina, 20);person a3(knaz, 40);person a4(nana, 20);person a5(yomi, 1);// 尾插到数组中arr.push_back(a1);arr.push_back(a2);arr.push_back(a3);arr.push_back(a4);arr.push_back(a5);// 打印数组中的每个元素的数据与大小容量print2(arr);cout capacity arr.get_capacity() endl;cout size arr.get_size() endl;// -----------------------------------------------------// 尾删2个元素arr.pop_back();arr.pop_back();// 打印数组中的每个元素的数据与大小容量print2(arr);cout capacity arr.get_capacity() endl;cout size arr.get_size() endl;
} 至此全部完成
