在正式开始学习系统学习C++前,我自己有在网上相关网站学了一些知识,所以在开始这项任务前我先看了布置下来的C++作业,看看自己能做多少。倒是有了些许收获。
重载函数与模板函数
在做作业时发现第二题出现了一个我没见过的词--“函数重载”。在百度后得到了解释:在传统的C语言中,函数名必须是唯一的,程序中不允许出现同名的函数。在C++中是允许出现同名的函数,这种现象称为函数重载。函数重载的目的就是为了方便的使用函数名。原理在于编译器会将函数名修饰,如将void func(int x)的函数名修饰成_func_int。而要实现函数重载必须要保证只有参数是唯一的区别,也就是返回值,函数名,作用域必须一样。
同时,我意外发现有几道利用重载函数的题目的题解,在那种帖子中发现贴主除了给出利用重载函数的题解。
#include <iostream>
using namespace std;
//比较int 类型
int Max(int a, int b)
{
return a > b ? a : b;
}
//比较char 类型
char Max(char a, char b)
{
return a > b ? a : b;
}
//比较float 类型
float Max(float a, float b)
{
return a > b ? a : b;
}
int main(void)
{
int n = 1;
int m = 2;
cout << "max(1, 2) = " << Max(n, m) << endl;
float a = 2.0;
float b = 3.0;
cout << "max(2.0, 3.0) = " << Max(a, b) << endl;
char i = 'a';
char j = 'b';
cout << "max('a', 'b') = " << Max(i, j) << endl;
return 0;
}
除此之外还给出了使用模板函数的方法,且这种方法比使用模板函数更简便。使用模板的主要目的是为了让程序员能够编写与类型无关的代码。例如,通过使用模板,我们可以编写一个既可以处理int类型又可以处理double类型的Max或Min函数。这样,我们就无需为每种数据类型都编写一个特定的Max或Min函数,从而提高了代码的效率和复用性。
函数模板定义形式
由以下三部分组成: 模板说明 + 函数定义 + 函数模板调用
template < 类型形式参数表 >
类型 函数名 (形式参数表)
{
//语句序列
}
#include <iostream>
using namespace std;
//template 关键字告诉C++编译器 要开始泛型编程了
//T - 参数化数据类型
template <typename T>
T Max(T a, T b) {
return a > b ? a : b;
}
int main(void)
{
int n = 1;
int m = 2;
cout << "max(1, 2) = " << Max(n, m) << endl;
float a = 2.0;
float b = 3.0;
cout << "max(2.0, 3.0) = " << Max(a, b) << endl;
char i = 'a';
char j = 'b';
cout << "max('a', 'b') = " << Max(i, j) << endl;
return 0;
}
于是我顺水推舟又去查了两者各自的优缺点,发现这的确是个简单的问题,无非是重载函数可以准确实现功能相似但数据类型或参数数量不同的同名函数,而模板函数则是更有效地提高了代码编写的效率,但是这样会增加编译时间。
在我看来,二者的关系就像循环和递归,在特殊情况下用递归要比循环方便快捷,不过模板函数在适用性上甚至更广泛。
C++面向对象的三大特性为:封装、继承、多态
一类物品中的具体某件事物便是对象,所以对于C++来说万事万物皆为对象。
1.封装
1.1封装的形式
class 函数名
{
//访问权限
//属性
//行为(通常为函数)
}; //注意末尾的分号
示例:创建一个圆类,获取圆的周长
#include <iostream>
using namespace std;
//圆周率
const double PI = 3.14159;
//设计一个圆类,求圆的周长
//圆求周长的公式 : 2 * PI * 半径
class Circle
{
//访问权限
//公共权限
public:
//属性
//圆的半径
int r;
//行为(通常为函数)
//计算圆的周长
double caculateZC()
{
return 2 * PI * r;
}
};
int main()
{
//通过圆类 创建具体的圆(对象)
Circle c1;
//给圆对象的属性进行赋值
c1.r = 10;
cout << "圆的周长为:" << c1.caculateZC () << endl;
return 0;
}
1.2访问权限
访问权限通过把类的属性和行为放在不同情况下加以控制。
访问权限有三种:
1.public 公共权限 类内可以访问,类外可以访问
2.proetected 保护权限 类内可以访问,类外不可以访问
3.private 私有权限 类内可以访问,类外不可以访问
1.3struct和class的区别
struct和class的唯一区别在于默认访问权限不同
struct 默认权限为 公共 public
class 默认权限为 私有 private
1.4将成员属性设置为私有
在企业中时常会用到这种做法,也就是将成员属性设置为私有后,通过一些操作限制用户的行为。
1.4.1控制读写程序
由于将成员属性设置为了私有,这样在类外我们是无法直接访问的,这时便可以通过一个公共属性的函数来实现读写操作
#include <iostream>
#include <string>
using namespace std;
//成员属性设置私有
//1、可以自己控制读写程序
//人类
class Person
{
public:
//设置姓名
void setName(string name)
{
m_Name = name;
}
string getName()
{
return m_Name;
}
//获取年龄
int getAge()
{
return m_Age;
}
//设置偶像
void setIdol(string idol)
{
m_Idol = idol;
}
private:
string m_Name; //姓名 可读可写
int m_Age = 18; //年龄 只读
string m_Idol; //偶像 只写
};
int main()
{
Person p;
//姓名设置
p.setName("张三");
cout << "姓名:" << p.getName() << endl;
//获取年龄 只读
cout << "年龄:" << p.getAge() << endl;
//偶像设置 只写
p.setIdol("法外狂徒");
//cout << "偶像:" << p.setIdol() << endl; 只写状态 外界访问不到
return 0;
}
1.4.2检测数据有效性
我们通过函数实现数据读写,自然也可以在这个函数中判断、检测数据的有效性。
下面是一段示例代码
void setAge()
{
int age;
while (1)
{ cout << "请输入年龄:";
cin >> age;
if (age < 0 || age > 150)
{
cout << "年龄输入有误,似乎不太符合常理" << endl;
continue;
}
else
{
m_Age = age;
break;
}
}
}
显然,这个函数确保了写入的年龄在0~150,符合条件则正确写入,反之则会循环重复输入。
2.对象特性
2.1构造函数和析构函数
对象的初始化和清理是程序中十分重要的安全问题,同时也是编译器要求必须做的事。
如果我们没有提供构造和析构函数,编译器也会提供,这种情况叫空实现
构造函数语法: 类名(){ }
1.构造函数,没有返回值也不写void
2.函数名称与类名相同
3.构造函数可以有参数,因此可以发生重载
4.程序在调用对象时候会自动调用构造,无需手动调用,而且只会调用一次
#include<iostream>
using namespace std;
class Student
{
public:
Student() {} //无参构造
Student(float x, float y) //有参构造
{
chinese = x;
math = y;
}
float chinese, math;
};
int main()
{
Student stu1;
Student stu2(80, 90); //Student实例stu2
cout << stu2.chinese << ' ' << stu2.math << endl; //输出:80 90
return 0;
}
析构函数语法: ~类名(){ }
1.析构函数,没有返回值也不写void
2.函数名称与类名相同,在名称前加上符号 ~
3.析构函数不可以有参数,因此不可以发生重载
4.程序在对象销毁前自动调用析构,无需手动调用,而且只会调用一次
#include<iostream>
using namespace std;
class Student
{
public:
~Student() //析构函数
{
cout << "调用析构咯" << endl;
}
private:
float chinese, math;
};
int main()
{
Student s1; //输出:调用析构咯
return 0;
}
2.2构造函数的分类以及调用
构造函数分为普通构造和拷贝构造,其中普通构造包括有参构造和无参构造。
构造函数的调用有三种方法:
- 括号法
- 显示法
- 隐式转换法
1.括号法 Person p1; Person p2(10); //有参构造 Person p3(p2); //无参构造 2.显示法 Person p1; Person p2 = Person(10); //有参构造 Person p3 = Person(p2); //无参构造 /* Person(10)为匿名对象,该行执行完后就回收 */ /* Person (p) === Person p; */ 3.隐式转换法 Person p1 = 10; //有参构造 Person p1 = Person(10); Person p3 = p2; //拷贝构造
2.3构造函数调用规则
默认情况下,C++编译器至少给一个类添加三个函数:无参构造,有参构造,拷贝构造
在这里有一个C++的调用规则
- 如果已经定义了有参或无参,编译器不再提供另一个的默认函数
- 如果已经定义了拷贝构造函数,编译器不再提供剩余两个构造函数
这一规则可以抽象为一个优先级的对比:拷贝构造>有参构造=无参构造
2.4浅拷贝与深拷贝
浅拷贝:简单的赋值拷贝操作,包括编译器提供的默认拷贝构造函数
深拷贝:在堆区重新申请空间,进行拷贝操作
关于深拷贝的使用,具体体现在,当类中有一个指针且需要拷贝这个指针时,由于拷贝的是指针的值,在两个对象调用析构函数时就会重复释放同样的内存,这是一个非法操作.
所以在拷贝函数中为m_height=new int(*p.m_height)而非m_height = p.m_height
2.5初始化列表
与普通构造函数差别不大
语法:构造函数( ) :属性1(值1) , 属性2(值2). . . { }
int a=10,b=20,c=30;
Person():m_A(10),m_B(20),m_C(30)
{
}
Person(int a,int b,int c):m_A(a),m_B(b),m_C(c)
{
}
2.6类对象作为类成员
类对象也可以作为类成员使用,正如结构体中可以嵌套定义结构体一样。
在程序运行时,类的构造与析构顺序与遵循栈区的特性,即“先进后出”。
2.7静态成员
*静态成员变量
- 所有成员共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
*静态成员函数
- 所有成员共享同一个函数
- 静态成员函数只能访问静态成员变量
静态成员可以通过类访问------类名::变量或函数
静态成员也是有访问权限的
C++基础:函数重载与模板函数详解
本文介绍了C++中函数重载的概念,以及模板函数的使用,讨论了它们在功能和效率上的区别。同时,对C++面向对象的封装、继承和多态特性进行了概述,包括构造函数、析构函数、浅拷贝与深拷贝等概念。
1321

被折叠的 条评论
为什么被折叠?



