目录
继承
继承的意义是可以用一个类来定义另一个类,这种方法达到了重复利用代码的目的,也便于修改和维护程序。
继承的基本用法:
class parent //父类
{
public:
int num_1;
void fun_1();
protected:
int num_2;
void fun_2();
private:
int num_3;
void fun_3();
};
class son: public parent //子类以public的形式继承父类
{
public:
int num_4;
void fun_4();
};
class parent_2 //父类2
{
public:
int num_5;
void fun_5();
};
class son_1: public parent, public parent_2
{
public:
int unum_6;
void fun_6();
};
在上述示例中,son为子类,继承了父类parent的内容,以public的形式继承,在继承时一般使用public,还有protected和private两种继承方式,这三种方式的区别主要是:
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
一个子类可以继承多个父类,如上述代码中所示。
注:继承中存在一些例外情况:
无法继承基类的构造函数、析构函数、拷贝构造函数
无法继承基类的重载运算符
无法继承基类的友元函数
参数省缺
参数省缺的意思是函数再定义或声明时为函数的参数设定一个默认值,在调用函数的时候,如果没有制定实参,则会用改默认值,若给定实参,则用实参的值。
省缺时可用全部省缺,也可用部分省缺,但部分省缺的时候省缺的参数只能从后往前省。
省缺值必须是常量后者全局变量
int N = 9; //v是全局变量
void func(int a, int b = 6, int c = N)
{
cout<<a<<" "<<b<<" "<<c<<endl;
}
func(3)
3 6 9
fun(3,4)
3 4 9
fun(3,4,5)
3 4 5
引用
在函数里常用,但是放到外面单独用乍一看有点陌生,就也记一下。
引用就说给既有变量取别名,不会开辟新的空间,所以改变引用值也会改变本体(引用在定义的时候必须初始化,且只能引用一个实体,不能换,但是一个变量可被多个引用)
int a = 0;
int& b = a;
int& c = a;
即b引用了a,函数里改变输入参数的值就 要用引用
拷贝构造函数
拷贝构造函数就是构造函数的重载,一般用一个对象定义另一个对象,但是必须要用引用,如果不用引用的话就会造成无限递归。不写的话直接用就是浅拷贝,对浅拷贝而言,析构的时候会造成一个指针释放两次的问题(因为两个指针指向了同一个地址),所以对这种情况而言,需要深拷贝,就是自己写一下。
class copy_fun{
public:
int x;
int* y;
char* z;
copy_fun(int a, int b, char& c){
x = a;
y = new int;
*y = b;
z = new char[strlen(c)+1];
strcpy(z,c);
}
copy_fun(copy_fun &per){
x = per.x;
y = new int;
*y = *per.y;
z = new char[strlen(per.z)+1];
strcpy(z,per.z);
}
}
运算符重载
运算符重载说白了就是把运算符当成函数,给他写个重载
bool operator==(const Date& D1, const Sate& D2){
return D1.year == D2.year
&& D1.month== D2.month
&& D1.day== D2.day
}
Date& operator=(const Date& d){
if(this != &d)
{
year = d.year;
month = d.year;
day = d.day;
}
return *this;
}
当运算符重载作为类成员的重载函数时,存在一个默认的形参this,作为运算符左边的参数,右边的参数要用const修饰。
返回值要使用返回引用,形成链式变成,即return *this,这个时候返回的是指针,所以用的时候要解引用。
初始化和赋值
初始化只能初始化一次,赋值可以赋值多次,所以初始化类的时候要用初始化列表进行初始化
每个变量只能初始化一次
引用成员变量;const成员变量;没有默认构造函数的类,需要在初始化列表进行初始化
成员便令在类中生命的顺序就是初始化列车中的初始化顺序,与其在初始化列表中的先后顺序无关,也就是说初始化的顺序是按照声明的顺序初始化的,不是按初始化列表的顺序。
class date{
public:
date(int a= 2022; int b= 4; int c= 29):year(a),month(b),day(c)
{}
private:
int year;
int month;
int day;
};
友元
友元函数:
一个类的友元函数可以访问该类的私有成员(可以理解为打破了被友元的类的封装,有时候外部想用的时候就可以用友元函数,但个人觉得不要 多用,不然类的封装就没有意义了)
可以将一个类的成员函数说明另一个类的友元
class A;
class B
{
int fun_B(A *a);
}
class A
{
friend int B::fun_B(A *a);
private:
int num;
}
int B::fun_b(A *a)
{
a->num = 100;
}
友元类:
若A是B的友元类,则A的成员函数可以访问B的私有成员
class A
{
friend class B;
private:
int num;
};
class B
{
public:
A a;
void fun()
{
a.num = 100;
}
}
注意:
友元之间的关系不能传递,不能继承。A是B的友元,B是C的友元,不代表A是C的友元。
内部类:定义在一个类内部的另一个类就叫做内部类,内部类相当于他的外部类的友元,内部类可以调用外部类的参数,但外部类不能用内部类的成员。
虚函数
虚函数的作用是,如果基类的指针指向派生类的函数,如果是普通函数,则会执行基类中的函数,如果是虚函数,则会执行派生类中的函数,
通过在成员函数前加修饰符virtual将成员函数标识为虚函数,虚函数是在类的定义中给出的。
延迟:如果一个函数是虚函数,并且在其派生类中对该函数进行了新的定义,那么该派生类的任何对象总会调用该派生类中所定义的虚函数,即使另外一个间接调用该函数的继承函数也会调用派生类中所定义的虚函数。这种可以选择使用哪个虚函数的方式被成为延迟。
多态:多态是指通过所谓的延迟绑定机制(虚函数先声明,在定义)将多个实现方式关联到一个函数名的能力。
覆盖:在一个派生类中改变了基类中虚函数的定义,则称此虚函数被覆盖了。(只有虚函数被成为覆盖,不是虚函数则成为重定义)
重写:重写是在继承中,子类可以定义和父类相同的名称并且参数列表一致的函数。(即覆盖,这么写是因为之前面试没想起来重写就是覆盖,所以分开写)
虚函数是多态实现的重要方式,调用的函数是程序在运行的时候动态确定的,而不是在编译的时候静态确定的。
使用的时候,简单来说就是在父类里定义一个虚函数,知道要这个函数,但是不知道在不同的子类里,这个函数的具体实现是什么样的,所以用虚函数的形式,先声明一个,在子类里实际要用的时候,再用实际子类定义的函数即可。
class Base
{
public:
virtual void vir_func()
{
cout << "This is virtual function,Base" << endl;
}
void func()
{
cout << "This is normal function,Base" << endl;
}
};
class A :public Base
{
public:
virtual void vir_func()
{
cout << "This is virtual function,A" << endl;
}
void func()
{
cout << "This is normal function,A" << endl;
}
};
class B :public Base
{
public:
virtual void vir_func()
{
cout << "This is virtual function,B" << endl;
}
void func()
{
cout << "This is normal function,B" << endl;
}
};
class C :public A
{
public:
void vir_func()
{
cout << "This is normal function,C" << endl;
}
};
Base *base = new Base;
A *a = new A;
B *b = new B;
C *c = new C;
base->func();
a->func();
b->func();
c->func();
cout << "*********************" << endl;
base->vir_func();
a->vir_func();
b->vir_func();
c->vir_func();
cout << "*********************" << endl;
((A *)b)->func();
((A *)b)->vir_func();
delete base;
delete a;
delete b;
运行结果为:
简单来说就是普通函数执行的是指针类型下的函数,虚函数执行的是指针指向的实际类型下的函数。(在父类定义虚函数的时候必须要有定义,但可是是空着的)
虚函数属性可以被继承(C就能看出来)
纯虚函数:不用定义,但也不能用它实例化对象(应该是,没有文献这么说,但我试了一下发现是这样)
class Base
{
public:
virtual void vir_func() = 0;
//{
// cout << "This is virtual function,Base" << endl;
//}
void func()
{
cout << "This is normal function,Base" << endl;
}
};
class A :public Base
{
public:
virtual void vir_func()
{
cout << "This is virtual function,A" << endl;
}
void func()
{
cout << "This is normal function,A" << endl;
}
};
class B :public Base
{
public:
virtual void vir_func()
{
cout << "This is virtual function,B" << endl;
}
void func()
{
cout << "This is normal function,B" << endl;
}
};
class C :public A
{
public:
void vir_func()
{
cout << "This is normal function,C" << endl;
}
};
//Base *base = new Base;
A *a = new A;
B *b = new B;
C *c = new C;
//base->func();
a->func();
b->func();
c->func();
cout << "*********************" << endl;
//base->vir_func();
a->vir_func();
b->vir_func();
c->vir_func();
cout << "*********************" << endl;
((A *)b)->func();
((A *)b)->vir_func();
//delete base;
delete a;
delete b;