阅读提示:本文章是关于c++相对于c语言增加的内容。该文章必要时会给出相应的代码方便大家理解。
目录
一、引用(c++新引入)
1、概念:
引用是一种特殊的别名,它为存在的变量提供了一个另外的名字。通常是用于函数传参的。
2、特性:
引用一旦初始化后,不能再更改为其他变量。
引用不是变量,所以不需要在定义时赋初值。
引用的操作就是对引用的变量进行操作。
#include<iostream>
using namespace std;
void change(int &x,int &y);
int main(){
int a=5,b=9;
int &c=a;
cout<<c<<endl; //c相当于是a的别名
change(a,b);
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
return 0;
}
void change (int &x,int &y){
int t=x;
x=y;
y=t;
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
}
根据代码可得出,函数参数为引用时,意味着函数内部对引用的操作会直接影响到传递给函数的实参。
二、函数
1、函数重载与函数模板
函数重载
1)概念:函数重载指的是在同一作用域内可以存在多个同名的函数,但这些函数的参数个数、类型、参数顺序必须是不同的,编译器会根据传递给函数的参数来决定调用哪个重载版本。
2)注意
函数的返回类型不能作为重载的依据。
使用默认参数可能会引起重载的歧义。
引用和指针可以用来区分不同的重载函数。
#include<iostream>
using namespace std;
int jf(int a,int b){ //1
return a-b;
}
double jf(int a,double b){ //2
return a-b;
}
double jf(double b,int a){ //3
return a-b;
}
int jf(int a,int b,double c){ //4
return a-b-c;
}
int main(){
int x=14,y=9,z=45;
double m=9.45,n=7.22;
cout<<"x-z="<<jf(x,z)<<endl; //1
cout<<"y-m="<<jf(y,m)<<endl; //2
cout<<"n-y="<<jf(n,y)<<endl ; //3
cout<<"x-y-z="<<jf(x,y,z)<<endl; //4
return 0;
}
函数模板
1)定义:允许定义一个可以处理一个或者多个数据类型的函数,函数模板的语法基于一个或者多个模板参数,在调用时会被具体的数据参数类型所代替。
#include<iostream>
using namespace std;
//函数模板的基本语法
/*
template<typename T>
返回类型 函数名(参数列表){
……
}
*/
template<typename T1,typename T2>
T1 add(T1 a,T2 b) {
return a+b;
}
int main(){
int x=23,y=8;
double m=9.332,n=5.91;
cout<<add(x,y)<<endl;
cout<<add(m,x)<<endl;
cout<<add(x,m)<<endl;
cout<<add(n,m)<<endl;
return 0;
}
2、内联函数
1)定义:内联函数建议编译器在每次调用该函数的地方直接展开函数体,减少了函数调用的开销。
2)特点:
普通函数调用涉及到栈的压入和弹出操作,而内联函数可以避免这些开销。
增加代码的大小,由于函数体会被展开,因此可能会增加程序的大小。
使用inline关键字是程序元对编译器的建议,编译器是可以忽略这个建议的,
3)注意
内联函数一般是那些简短的函数才会被定义。
内联函数不应该被递归,不能含有switch和循环
//这些注意点都与特点2有关
下面是内联函数的语法定义:
inline 返回类型 函数名(参数列表){
//函数体
}
三、类与对象
1、基本概念
- 类(class):类是面向对象编程的基础构建块,是定义对象属性(数据成员)和行为(成员函数或方法)的蓝图,类定义了对象的类型,并创建了该类型的对象提供模板。
- 对象:是类的实例,当你创建一个对象时,实际是创建一个具体的数据结构,它具有类定义中指定的属性和行为。
#include<iostream>
using namespace std;
#define pi 3.14
class circle{ //circle是类名
public: //访问控制修饰符 (公有)
float area; //成员变量
float size;
float r;
float Carea(float r) { //成员函数
return pi*r*r;
}
float Csize(float r){
return 2*pi*r;
}
};
int main(){
circle cl;
cout<<"输出圆的半径:"<<endl;
cin>>cl.r;
cl.area=cl.Carea(cl.r);
cl.size=cl.Csize(cl.r);
cout<<"圆的面积:"<<cl.area<<endl;
cout<<"圆的周长:" <<cl.size<<endl;
return 0;
}
访问修饰符 | 含义 |
public | 公有成员变量和成员函数(外部可直接访问,比如 cl.size) |
private | 私有成员变量和成员函数(外部不可直接访问) |
protected | 保护成员变量和成员函数 |
2、类的封装
1)概念:是一种将对象的实现细节隐藏起来的机制,只暴露出有限的接口与外部进行交互
2)好处:数据隐藏,防止外部直接访问类的内部数据
只暴露必要操作,实现接口抽象
通过访问修饰符控制对成员的访问
3、构造函数与析构函数
构造函数
构造函数的概念:它是一种特殊的成员函数,在创建对象时自动被调用,构造函数的名称必须同类名一样,并且没有返回类型,甚至连void也没有。
构造函数作用:初始化对象的数据成员;执行创建对象所需的任何操作。
构造函数类型 | 含义 |
默认构造函数 | 没有参数的构造函数 |
参数化构造函数 | 接受一个或多个参数的构造函数 |
复制构造函数 | 用于创建一个对象用于另一个同类对象的副本 |
移动构造函数 | 用于从临时对象或者右值引用创建对象 |
析构函数
析构函数的概念:析构函数在对象被销毁时自动被调用。析构函数的名称是类名前加上波浪号(~),同样没有返回类型。
析构函数的作用:释放对象分配的资源,如动态分配的内存;执行对象销毁前所需的任何其他的操作。
#include<iostream>
using namespace std;
class rectangle{
private:
int *wt;
int *ht;
public:
int w;
int h;
//默认构造函数
rectangle(){
w=0;
h=0;
*wt=w;
*ht=h;
}
//参数化构造函数
rectangle(int a,int b){
w=a;
h=b;
}
//复制构造函数
rectangle(const rectangle& r) {
w=r.w;
h=r.h;
}
// 析构函数
~rectangle(){
delete wt;
delete ht;
}
//成员函数
int area () {
return w*h;
}
};
int main(){
rectangle r2(2,9);
rectangle r3(r2);
cout<<r2.area()<<endl;
cout<<r3.w<<endl;
cout<<r3.h<<endl;
return 0;
}
四、类的派生与继承
1、基本概念
派生是之指创建一个新的类(派生类、子类)的过程,这个新类基于一个已存在的类(基类、父类),继承基类的属性、方法,并添加新的属性和方法。
继承是一种使一个类称为(子类或派生类)能够继承另一个类(基类或父类)的特性和方法的关系
类的继承的方式与基类成员的访问权限的规则:
公有成员 | 保护成员 | 私有成员 | |
公有继承 | 公有 | 保护 | 不可访问 |
保护继承 | 保护 | 保护 | 不可访问 |
私有继承 | 私有 | 私有 | 不可访问 |
派生的类型:
1)单继承:派生类只有一个基类。
2)多继承:派生类有多个基类。
派生类的语法:
class 派生类名 : 继承方式 基类名{
//派生类的新成员和覆盖的方法
};
#include<iostream>
using namespace std;
//基类
class A{
public:
void c1(){
cout<<"hello"<<endl;
}
};
//派生类
class B:public A{
public:
void c2(){
cout<<"你好"<<endl;
}
//覆盖基类的方法
void c1(){
cout<<"hello world!"<<endl;
}
};
int main(){
A a;
B b;
a.c1();
b.c2();
b.c1();
return 0;
}
注意点
1)派生类构造和析构时会自动调用基类的构造和析构函数。
2)在单继承中,一个类不应该多次继承同一个基类。
3)在多继承中,如果多个基类继承于同一个类,可以使用虚继承来避免重复继承问题。
2、派生类的构造函数和析构函数
派生类的构造函数
派生类名 :: 派生类名(参数列表):基类名(参数列表){
//构造函数
}
派生类的析构函数
派生类名::~派生类名(){
//析构函数
}
3、多重继承
class 派生类名 :继承方式 基类1,继承方式 基类2
{
//派生类的新成员的和覆盖的方法
};
#include<iostream>
using namespace std;
class A{
public:
void c1(){
cout<<"你好,";
}
};
class B{
public:
void c2(){
cout<<"中秋节";
}
};
class C{
public:
void c3(){
cout<<"快乐"<<endl;
}
};
class D: public A,public B,public C
{
public:
void c4(){
cout<<"中国万岁"<<endl;
}
};
int main(){
D d;
d.c1();
d.c2();
d.c3();
d.c4();
return 0;
}
4、虚基类
作用:虚基类是用于解决多继承可能发生的钻石问题的一种机制。钻石问题是指多继承的层次结构中,一个类从两个或多个不同路径继承同一个基类,这导致的问题是基类成员在派生类中存在多个副本。
注意点:
1)使用虚基类时,确保构造函数的正确调用路径。
2)因为虚基类的使用会是程序更为复杂,所以只有确定要解决钻石问题才可以使用。
3)在多继承体系中,对虚基类的指针或引用的访问可能需要显式指定。
虚基类的定义
class 派生类名 :virtual 继承方式 共同基类名
#include <iostream>
using namespace std;
class A{
public:
int a;
int b;
void c1(){
cout<<a+b<<endl;
}
};
class B:virtual public A{
public:
void c2(){
cout<<a-b<<endl;
}
};
class C:virtual public A{
public:
void c3(){
cout<<a*b<<endl;
}
};
class D:public B,public C{
public:
void c4(){
cout<<b%a<<endl;
}
};
int main(){
D d;
d.a=23;
d.b=9;
d.c1();
d.c2();
d.c3();
d.c4();
return 0;
}
五、多态性
1、定义
多态性是指同一操作作用于不同对象可以有不同的表现形式。具体表现形式为不同功能的函数有同一个函数名,这样实现了用同一个函数调用了不同内容的函数。多态性主要是通过虚函数和继承来实现。
2、虚函数
定义:
虚函数是一个基类中被声明为virtual的成员函数,它可以在派生类中重新被定义。当用基类的指针或者引用来调用这个函数时,程序将根据对象的实际来决定调用哪个函数版本。
class A{
public:
virtual void say(){
cout<<"hello";
}
};
在基类中用关键字virtual声明函数称为虚函数,当一个类包含虚函数时,编译器会为该类创建一个虚函数表(vable),其中存储了该类及其派生类中所有虚函数的地址。
在派生类中重写(overrider)基类的虚函数,以实现不同的行为。当通过基类指针或引用调用虚函数时,实际调用的是派生类中重写的函数,从而实现多态性。
同时,在派生类关键字(final)用于限制类的继承和虚函数的重写,如果一个类/虚函数被标记为final,则该类/虚函数不能继承/重写。
class A{
public:
virtual void s() final{
cout<<"good"<<endl;
}
};
class B:public A{
public:
//这里会报错,因为A中虚函数被标记为final,所以不可重写
void s() override(){
cout<<"very good"<<endl;
}
};
#include<iostream>
using namespace std;
class A{
public:
virtual void s1(){
cout<<"123"<<endl;
}
virtual void s2() {
cout<<"hello"<<endl;
}
};
class B:public A{
public:
void s1() { //覆盖
cout<<"12345"<<endl;
}
void s2() override{ //重写
cout<<"hello world"<<endl;
}
};
int main(){
A* ptr=new B();
ptr->s2(); //调用派生类的s2函数 ,实现多态
*ptr;
B b;
b.s1();
return 0;
}
3、纯虚函数与抽象类
纯虚函数定义:指的是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现的纯虚函数的方法是在函数原型后加=0
virtual void s()=0
抽象类:包含纯虚函数的类称为抽象类
4、运算符重载
运算符重载是一种机制,允许程序员为自定义数据类型定义运算符的行为,意味着你可以在数据类型上使用标准的c++运算符
语法如下:
class 类名{
public:
//构造函数、析构函数、成员函数……
//运算符重载函数
类名 operator运算符 (参数列表) const{
//运算符重载的实现
}
};
运算符重载规则
1)运算符的基本含义和优先级不变
2)要有返回类型,且是类名或者类名的一个对象
3)运算符重载函数的参数必须是自定义数据类型
#include<iostream>
using namespace std;
class A{
public:
int x,y;
A(int a,int b):x(a),y(b){}
A operator+(const A& ptr)const{
return A(x+ptr.x,y+ptr.y);
}
A operator-(const A& ptr)const {
return A(x-ptr.x,y-ptr.y);
}
};
int main(){
A c1(3,5),c2(34,77);
A c3=c1+c2;
A c4=c2-c1;
cout<<c3.x<<endl;
cout<<c3.y<<endl;
cout<<c4.x<<endl;
cout<<c4.y<<endl;
return 0;
}
/*好久没有更新了,前不久参加了数学建模,感觉身体遭不住。言归正传,我们学校的进度有点慢,这篇文章算是为我和我室友后面复习做的一些准备,由于c++的内容实在太多了,有些内容我进行了简化,后面还有文件操作的一些内容我没写,后面等有机会再完善*/