前言:适合期末理解性复习概念,查缺补漏,知识点写的比较简洁。文内有什么缺漏或错误欢迎指正!
一、c++基础知识点(主要是容易忽略的小地方)
1、c++是面向对象程序设计
2、void通常表示无值,即不需要返回值
3、使用const定义常量(c中使用#define来定义常量),const可以和指针一起使用分为三类
(1)指向常量的指针:const char* p="abd";--const挨着char所以不能改变char定义的abd
(2)常指针:char* const p="abd"--const挨着p所以不能改变p的地址(指针指向的是地址)
(3)指向常量的常指针const char* const p="abd";--const挨着char和p所以abd和p的地址都不能够改变
4、内联函数inline:消除调用函数时的系统开销,以提高程序的运行速度
5、带有默认参数的函数:没默认值的在左边,有默认值在右边
6、::作用域标识符,相当于**(类/局部)的东西(eg:A::func()A的函数func())
7、强制类型转换
int i=10;
double x=(double)i;//两种都可以
double y=double(i);
8、new和delete运算符
有new必须要有delete释放空间,否则会变成死空间
如果new的内存空间不够,new将返回空指针NULL
//常用的指针
int *p;
p=new int;
delete p;
//数组
int *p;
p=new int[10];
delete []p;//[]可有可无
//new可以初始化
int *p;
p=new int(12);//初始化的值为12
delete p;
9、引用(别名)
int i=10;
int &j=i;//j相当于i的一个别名,i和就的地址和数值都是一样的
引用作为函数参数,使用引用返回函数值
#include<iostream>
#include<string>
using namespace std;
void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
int main()
{
int a=10;
int b=20;
swap(a,b);//这里的ab是引用传递
cout<<a<<b<<endl;//这里输出ab的值为20、10
}
注意:
不允许建立void类型的引用
不能建立引用的数组
不能建立引用的引用(不能建立指向引用的指针)
10、class类默认访问权限private/struct结构体默认访问权限public
11、this指针:每个成员函数都有一个特殊的指针this,它始终指向当前被调用的成员函数操作的对象
二、函数重载
函数的参数类型/参数个数不同都可以进行函数重载
#include<iostream>
#include<string>
using namespace std;
void func(int a)//void 不需要返回值
{
cout<<"func(int a)的重载"<<endl;
}
void func(char c)//参数类型不同
{
cout<<"func(char b)的重载"<<endl;
}
void func(int a,int b)//参数的个数不同
{
cout<<"func(int a,int b)的重载"<<endl;
}
int main()
{
int a;
int b;
char c;
func(a);//d调用func(int a)
func(c);//调用func(char c)
func(a,b);//调用func(int a,int b)
}
注意:返回值类型不同不能重载
有默认值的函数重载可能会发生二义性
void func(int r,int j=0);
void func(int r);//这样的两个函数时不可以重载的 具有冲突
调用函数时如果给出的实参和形参的数据类型不相符,编译器会自动转换,成功就运行,不成功报错
void func(int a);
void func(long a);
func(12.34);//这个12.34既不是int类型也不是long类型,这个时候编译器会自动转换
三、类和对象
类内包括数据成员和成员函数
问权限有三种(private;protect;public;)
1、成员函数
函数写在类内
class Base
{
public:
void func()//函数直接写在类内
{
cout<<"类内函数"<<endl;
}
};
函数类内声明类外定义
//函数类外定义格式:返回值类型 类名::成员函数(参数列表)
class Base
{
public:
void func(int x,int y);//类内声明
}
void Base::func(int x,int y)//类外定义
{
cout<<a+b<<endl;
}
构造函数
无参构造函数+有参构造函数
类名(参数列表){}
class Base
{
private:
int a;
public:
Base(){}//无参构造函数
Base(int b)//有参构造函数
{
a=b;
cout<<"Base(int a)有参构造函数的调用"<<endl;
}
};
析构函数(没有参数)
析构函数:~类名(){}
class Base
{
public:
Base(){}//无参构造函数
~Base(){}//析构函数
};
2、静态成员
static关键字开头,多个类共享
static成员属于类,不属于某个具体的对象
静态成员函数只能访问类中的静态数据成员
(1)静态数据成员:类内声明 类外定义
//静态数据 类内声明类外定义
class Base
{
static int a;
};
Base::static int a=100;
(2)静态成员函数:类内声明类外定义
//静态成员函数:类内声明 类外定义
class Base
{
static void func();
};
Base:: static void func()
{
cout<<"static void func()的调用"<<endl;
}
3、友元
友元函数(friend)
友元函数不是类的成员函数,所以没有this指针,必须通过参数传递对象
友元函数只能通过形参传递进来的对象或者对象指针来引用该对象的成员
class Base
{
public:
friend void func(int a);//友元函数
};
void func(int a)
{
cout<<"Based的friend友元函数"<<endl;
}
友元类
友元类中的所有成员函数都是另一个类中的友元成员
语法:friend class 类名
- 类之间的友元关系不能够传递
- 类之间的友元关系是单向的
- 友元关系不能够被继承
class A
{
//类内功能
};
class B
{
public:
friend class A//友元类A
};
class C:public B//C继承B 后面会有
{
//类C并不会继承B中的友元类A
};
四、继承
1、继承
继承就是在一个类的基础上建立一个新的类,已有的称为基类/父类,新建立的类称为派生类/子类。(相当于父亲和儿子,儿子继承了父亲的某些特点)
继承:class 子类名:继承方式 父类名
继承方式/基类成员 | public成员 | protect成员 | private成员 |
public | public | protect | 不可见 |
protect | protect | protect | 不可见 |
private | private | private | 不可见 |
利用关键字using可以改变基类成员在子类中的访问权限(只能改变public和protect)
class Father
{
public:
void func(){}
protect:
int a;
private:
int b;
};
class Son:public Father
{
public:
using Father::a;//将父类的protect权限的数据成员a修改成public权限
//using Father::b; 错误!!!!using不能够修改private的权限
private:
using Father::func;//将父类的public成员函数修改成private
};
2、多继承
一个子类同时继承多个父类
多继承会让代码逻辑混乱,后面的Java、C#、PHP等都取消了多继承。
格式:class 子类名:继承方式 父类1,继承方式 父类2......
同样的子类继承父类的除了private权限的数据成员和成员函数
3、子类的构造函数和析构函数
先执行父类的构造函数然后执行子类的构造函数,先执行子类的析构函数在执行父类的析构函数
子类的构造函数格式:子类类名(总参数列表):父类名(父类参数列表),子对象1(参数列表){构造函数体;}
#include<iostream>
#include<string>
using namespace std;
class Father
{
private:
int a;
public:
Father(int aa):a(aa)
{
cout << "Father的构造函数调用" << endl;
}
~Father()
{
cout << "Father的析构函数的调用" << endl;
}
};
class Son:public Father
{
private:
double b;
public:
Son(int a,double bb):Father(a),b(bb)
{
cout << "Son的构造函数的调用" << endl;
}
~Son()
{
cout << "Son的析构函数的调用" << endl;
}
};
int main()
{
Son* S = new Son(7, 99.9);
delete S;
return 0;
}
4、虚基类
子类在继承简洁公共父类时只保留一份同名成员
格式:class 子类类名:virtual 继承方式 父类名
五、运算符重载
1、本质
运算符重载的本质是函数重载
重载格式:返回值类型 operator 符号(传参列表)
规则:重载运算符时,运算符的运算顺序和优先级别不变,操作个数不变。只能重载c++内已有的运算符,一般来说重载的函数运算符和本身的含义差不多(比如说+,重载的含义就是加法)
. | 类属关系运算符 |
* | 成员指针运算符 |
:: | 作用域运算符 |
?: | 条件运算符 |
# | 编译预处理运算符 |
sizeof | 求数据类型长度运算符 |
2、重载运算符例子
(1)重载赋值运算符运算符
//赋值运算符重载
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(int age)
{
Age = new int(age);
}
~Person()//堆区内存重复释放,程序崩溃,需要重新开辟第二个空间
{
if (Age != NULL)
{
delete Age;
Age = NULL;
}
}
Person& operator=(Person& p)
{
if (Age != NULL)
{
delete Age;
Age = NULL;
}
Age=new int(*p.Age);//Age=p.Age
return *this;
}
int* Age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1;//赋值操作
cout << *p1.Age << endl;
cout << *p2.Age << endl;
cout << *p3.Age << endl;
}
int main()
{
test01();
return 0;
}
(2)重载算术运算符
以++运算符为例子
//++运算符重载
#include<iostream>
#include<string>
using namespace std;
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
Num = 10;
}
//重载前置++运算符
MyInteger & operator++()//返回引用可以一直对一个数据递增,链式
{
Num++;
return *this;//返回自身
}
//后置递增
MyInteger operator++(int)//发生了重定义,返回值不可以满足重载调节,加一个int占位参数,可以用于区分前置和后置递增
{
//先记录结果
MyInteger temp = *this;
//后递增
Num++;
//最后返回记录结果
return temp;
//后置递增返回的是值,前置返回的一定是引用
}
private:
int Num;
};
ostream & operator<<(ostream& cout, MyInteger myint)//myint 相当于一个int类型的数据不需要加&
{
cout << myint.Num ;
return cout;
}
void test01()
{
MyInteger myint;
cout << ++myint << endl;
}
void test02()
{
MyInteger myint;
cout << myint++<< endl;
cout << myint << endl;
}
int main()
{
//test01();
test02();
}
(3)重载其它运算符
左移运算符重载为例子
//左移运算符重载
#include<iostream>
#include<string>
using namespace std;
//左移运算符重载
class Person
{
friend ostream & operator<<(ostream& cout, Person& p);
public:
//利用成员函数重载p.operator<<(cout)简化版本p<<cout
/*ostream operator<<(Person &p)
{
//一般不会使用成员左移运算符函数重载,因为无法实现cout在左侧
}*/
int A;
int B;
};
//只能利用全局函数重载左移运算符
ostream & operator<<(ostream &cout, Person& p)//简化就是cout<<
{
cout << p.A<<endl;
cout << p.B << endl;
return cout;
}
void test01()
{
Person p;
p.A = 10;
p.B = 20;
cout << p<<endl;//用的void是不能持续追加的,但是改成ostream返回的就是cout就可以持续输出
}
int main()
{
test01();
}
函数调用运算符重载(不常见,可以了解一下)
#include<iostream>
#include<string>
using namespace std;
class MyPrint
{
public:
void operator()(string test)//仿函数(函数调用运算符重载)
{
cout << test << endl;
}
};
void MyPrint02(string test)
{
cout << test << endl;
}
void test01()
{
MyPrint myprint;
myprint("hello world");
}
class Add
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
void test02()
{
Add add;
int ret = add(10, 25);
cout << ret << endl;
//匿名函数对象
cout << Add()(100, 200) << endl;//用完就释放
}
int main()
{
//test01();
//MyPrint02("Priint");
test02();
}
六、多态和虚函数
1、虚函数
作用:实现程序多态性,使用父类对象指针访问子类对象的同名函数
格式:virtual 函数类型 函数名(参数列表){};/ 函数类型 函数名(参数列表)=0;
class A
{
public:
virtual void func()
{
cout<<"A func()"<<endl;
}
};
class B:public A
{
public;
void func()//重写虚函数
{
cout<<"B func()"<<endl;
}
};
int main()
{
B b;
b.func();//输出B func()
A *a=&b;
a->func();//输出B func() 如果没用virtual虚函数就会输出A func()
}
2、多态
(1)基础概念
子类的指针可以赋给父类指针
1、通过父类的指针调用父类和子类的同名虚函数时
- 若指针指向一个父类对象,那么调用的时父类的虚函数
- 若指针指向的时一个子类的对象,那么被调用的时子类的虚函数
2、引用
- 子类的对象可以赋给父类引用
- 通过父类引用调用基类和子类中的同名虚函数时,若引用父类的对象,调用父类的虚函数,若引用子类的对象,调用子类的虚函数。
#include<iostream>
#include<string>
using namespace std;
class A{
public:
virtual void func()=0;
};
class B:public A
{
public:
void func(){}
};
int main()
{
B b;
A *p=&b;//&是让p指针指向b的地址
p->func();//调用哪个虚函数取决于p指向哪种类型的对象
return 0;
}
- 静态多态:在程序编译时系统就决定调用哪个函数
- 动态多态:在程序运行的过程中决定调用哪个函数
(2)多态的作用
增强程序的可扩充性,即程序各个部分相对独立,出现错误可以部分修改。
七、函数模板和类模板
1、函数模板
(1)概念
建立一个返回值类型和形参类型不具体指定的通用函数,在调用函数的时候指定。
①
template <typename 类型参数>
返回类型 函数名(模板形参表)
{
函数体
}
②
template <class 类型参数>
返回类型 函数名(模板形参表)
{
函数体
}
#include<iostream>
using namespace std;
template <typename Type>
Type func(Type i){
i++;
return i;
}
int main(){
func<int>(5);//调用方式:类型写在<>内
}
2、类模板
建立一个返回值类型和形参类型不具体指定的通用类,在调用类的时候指定
template <class 类型参数>
class 类名
{
类内成员、函数……
}
template <typename T>
class A{
private:
T x;
public:
A(T y){
x=y;
}
T add(){
return x++;
}
};
3、重载的本质
重载本质来说就是定义一个不确定的数据类型,后期使用的时候再去定义变量类型。
当你需要改变变量类型时只需要改变使用时的变量类型。
4、重载的使用方法
函数:函数名<变量类型>()/也可以直接给变量 eg:add(2,3)
类:类名<变量类型>