day 7
完善类中的转化
#include <iostream>
#include<string.h>
using namespace std;
class mystring
{
public:
mystring(const char* s = nullptr):str(nullptr)
{
cout << " mystring()" << endl;
str = new char[strlen(s) + 1];
strcpy(str, s);
}
mystring& operator=(const mystring& another)
{
cout << "operator =" << endl;
int len = strlen(another.str);
this->str = new char[len + 1];
strcpy(this->str, another.str);
return *this;
}
private:
char* str;
};
int main()
{
mystring s1 = "china";
mystring s2 = "americn";
s2 = s1; //调用operator= 重载赋值运算函数
s1 = "abcd"; //也调用 operator=重载赋值运算,这里将“abcd”隐式转化为mystring类的类型,在转化的过程中又调用了构造函数
}
终端输出:
mystring()
mystring()
operator =
mystring()
operator =
来看点高级货
functor仿函数
把类对象像函数名一样使用。仿函数就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数了
语法:
class 类名
{
返回类型 operator()(参数列表)
{函数体}
}
例子:
#include <iostream>
#include <vector>
using namespace std;
class Pow
{
public:
int operator()(int i)
{
return i * i;
}
double operator ()(double d)
{
return d * d;
}
};
int main()
{
Pow pow;
int i = pow(4);
//可以理解为 pow.operator()(4),类比pow.operator+(b),可以写为 pow + b ,这里将operator省区,pow()(4),为了简便就写为power(4),这里可以理解为 ()的重载(()本省不可被重载),只不过重载的返回值使本类类型,而这里相当于是函数的返回值
double d = pow(5.5);
cout << i << endl;
cout << d << endl;
return 0;
}
终端输出:
16
30.25
functor 的优势在于,是对象形式,可以携带更多的的信息,用于作出判断。比如,我 们可以在对象初始化的时候,传入参数来决定状态,而不用去修改原代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Comp
{
public:
Comp( bool f = true):_f(f){}
bool operator()(int a,int b)
{
if(_f)
return a>b;
else
return a<b;
}
private:
bool _f;
};
int main()
{
int array[8] = {100,89,23,56,12,200,22,500};
vector<int> vi(array,array+8);
sort(vi.begin(),vi.end(),Comp(false));
for(auto i:vi)
{
cout<<i<<endl;
}
return 0;
}
智能指针
#include<iostream>
#include<memory> //auto_ptr<类名> 变量名(new 类名)的头文件
using namespace std;
class A
{
public:
A()
{cout << "A()" << endl;}
~A()
{cout << "~A()" << endl;}
void dis()
{cout<<"china"<<endl;}
private:
};
int main()
{
auto_ptr<A> p(new A); //自动释放内存,auto_ptr<A>相当于一个类名,生成了一个对象p,构造参数是z
A* a = new A; //只分配不释放
p->dis(); (*p).dis(); //调用类A的函数
}
终端输出:
A()
A()
~A()
智能指针的实现
class myauto_ptr
{
public:
myauto_ptr(A * ptr) //将传入的变量加入私有成员
{
_ptr = ptr;
}
~myauto_ptr() //销毁申请的空间
{
delete _ptr;
}
A * operator->() //对 -> 重载 返回类A的指针
{
return _ptr;
}
A & operator*() //对 * 重载 返回 类A的引用
{
return *_ptr;
}
private:
A * _ptr;
};
void foo()
{
myauto_ptr p( new A); //生成对象调用构造器,传入指向类A的指针
p->func(); //调用 类A的函数
(*p).func();
}
解释原理:自动销毁的原因就是利用的函数结束栈内存自动释放,而类对象的销毁要调用析构器,在析构器中释放堆内存。这里在栈上新建了myauto_ptr的对象ptr ,函数执行完对象ptr销毁,导致指向类A的匿名对象一并销毁
operator new/delete
适用于极个别的情况,一般很少用。比如定制内存
语法格式:
void *operator new(size_t)
void operator delete(void *)
void *operator new(size_t)
void operator delete[](void*)
例子
#include <iostream>
using namespace std;
class A
{
public:
A(){
cout<<"A constructor"<<endl;
}
~A(){
cout<<"A destructor"<<endl;
}
void * operator new (size_t size)
{
cout<<"new "<<size<<endl;
void *p = malloc(size);
((A*)p)->a = 100;
return p;
}
void operator delete(void *p)
{
cout<<"delete"<<endl;
free(p);
}
void * operator new[] (size_t size)
{
cout<<"new[] "<<size<<endl;
return malloc(size);
}
void operator delete[](void *p)
{
cout<<"delete[] "<<endl;
free(p);
}
void dis()
{
cout<<a<<endl;
}
private:
int a;
};
int main()
{
int *pi = new int;
A * pa = new A;
pa->dis();
return 0;
}
inheritance继承
引入:
在 C++中代码的可重用性(software reusability)是通过继承(inheritance)这一机制来实 现的。 如果没有掌握继承性,就没有掌握类与对象的精华。
#include <iostream>
using namespace std;
class Student
{
public:
void study(string course)
{
cout<<" i am a student , i am learning "<<course<<endl;
}
void eat(string food)
{
cout<<" i am eating "<<food<<endl;
}
};
class Teachers
{
public:
void tech(string course)
{
cout<<" i am teacher , i am teaching "<<course<<endl;
}
void eat(string food)
{
cout<<" i am eating "<<food<<endl;
}
};
观察上面代码,发现了两个类中的eat函数是一摸一样的,这代码是重用的,所以要进行改进
#include<iostream>
using namespace std;
class Human
{
public:
void eat(string food)
{
cout << food << endl;
}
};
class Student:public Human //Student的父类是Human
{
public:
void student(string course)
{
cout << "i am studenti i learn " << course << endl;
}
};
class Teacher :public Human //Teacher的父类是Human
{
public:
void teacher(string course)
{
cout << "i am teacher i teach " << course << endl;
}
};
int main()
{
Teacher t; Student s;
t.teacher("C++");
t.eat("方便面");
s.student("C++");
s.eat("干吃面");
return 0;
}
继承是一种设计的结果,通常是发生于一套类库中的,设计代码重用的方式,这种关系是一种设计而为之,不是想继承,就可随便继承的
语法:
class 派生类:[继承方式] 基类名
{
派生类成员声明;
};
默认的继承方式是private,一个派生类可以同时有多个基类,这种情况是多继承,派生类只有一个基类,称为单继承
继承方式:
#include<iostream>
using namespace std;
//继承方式影响了父类成员被子类对象的访问权限
class A
{
public:
int pub;
protected: //在类外访问与private一样,在public继承后可见(子类中可见)
int pro;
private:
int pri;
};
class B:public A
{
public:
void dis()
{
cout << pub << endl;
cout << pro << endl;
//cout << pri << endl; 父类私有成员 pri 在子类中不可见(不可访问)
}
int pubb;
protected:
int prob;
private:
int prib;
};
class C:public B
{
public:
void cc()
{
cout << pub << endl; //再次继承任然可以访问那些可见的成员且权限不变
cout << pro << endl;
}
};
int main()
{
A a; B b;
cout << b.pubb << endl;
cout << b.pub << endl; //pro ,pri不可访问
C c;
b.dis();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pH77DU0V-1655360070710)(E:\博客\photo\Snipaste_2022-05-27_17-23-34.png)]
派生类的组成
class A
{
public:
int pub;
protected: //在类外访问与private一样,在public继承后可见(子类中可见)
int pro;
private:
int pri;
};
class B:public A
{
public:
void dis()
{
cout << pub << endl;
cout << pro << endl;
//cout << pri << endl; 父类私有成员 pri 在子类中不可见(不可访问)
}
int pubb;
protected:
int prob;
private:
int prib;
};
int main()
{
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
}
终端输出:
12
24
继承是一种除了基类的构造器和析构器外,都全盘接受的的。基类可能使派生类冗余,所以基类是要设计的
#include<iostream>
#include<typeinfo>
using namespace std;
class A
{
public:
A() {
cout << "A()";
cout << &a << endl;
cout << "A-this " << this << endl;
cout <<"type A this "<< typeid(this).name() << endl;
}
private:
int a;
};
class B :public A
{
public:
B() {
cout << "B())";
cout << &b << endl;
cout << "B-this " << this << endl;
cout << "type B this " << typeid(this).name() << endl;
}
private:
int b ;
};
class C :public B
{
public:
C() {
cout << "C()";
cout << &c << endl;
cout << "C-this " << this << endl;
cout << "type C this " << typeid(this).name() << endl;
}
private:
int c;
};
int main()
{
C cc;
cout << &cc << endl;
}
终端输出:
A()00000038628FFD08
A-this 00000038628FFD08
type A this class A * __ptr64
B())00000038628FFD0C
B-this 00000038628FFD08
type B this class B * __ptr64
C()00000038628FFD10
C-this 00000038628FFD08
type C this class C * __ptr64
00000038628FFD08
派生类的构造
派生类中,由基类继承而来的成员的初始化工作,还是由基类的构造函数完成,然后 派生类中新增的成员在派生类的构造函数中初始化
语法:
派生类名::派生类名(总参列表)
:基类名(参数表),内嵌子对象(参数表)
{
派生类新增成员的初始化语句; //也可出现地参数列表中
}
解释:
#include<iostream>
#include<typeinfo>
using namespace std;
class A
{
public:
A() //或A(int i = 9)
{
cout << "A()" << endl;
}
private:
int a = 10;
};
class B:public A
{
public:
B(int i,int j)
{
cout << "B()" << endl;
}
private:
int b = 20;
};
int main()
{
B b(3,2);
}
子类的构造过程中,调用父类的构造器,标配(无参.重载或有默认参数)会被自动调用,没有标配就必须要显示调用,例如:
#include<iostream>
using namespace std;
class A
{
public:
A(int i)
{
cout << "A()" << endl;
}
private:
int a = 10;
};
class B:public A
{
public:
B(int i,int j)
:A(i) //实现对基类的构造
{
cout << "B()" << endl;
}
private:
int b = 20;
};
int main()
{
B b(3,2);
}
关于内嵌子对象:
//内嵌子对象有标配时直接调用
#include<iostream>
using namespace std;
class C
{
public:
C()
{
cout << "C()" << endl;
}
};
class A
{
public:
A(int i)
{
cout << "A()" << endl;
cout << i << endl;
}
private:
int a = 10;
};
class B:public A
{
public:
B(int i,int j,int k)
:A(k)
{
cout << "B()" << endl;
cout << i << " " << j << endl;
}
private:
int b = 20;
C cc; //cc就是内嵌子对象
};
int main()
{
B b(3,2,1);
}
终端输出:
A() //先初始化=基类
1
C() //在初始化内嵌子对象
B() //最后初始化自己
3 2
//无标配时,必须显示调用
#include<iostream>
#include<typeinfo>
using namespace std;
class C
{
public:
C(int m,int n)
{
cout << m << " " << n << endl;
cout << "C()" << endl;
}
};
class A
{
public:
A(int i)
{
cout << "A()" << endl;
cout << i << endl;
}
private:
int a = 10;
};
class B:public A
{
public:
B(int i,int j,int k,int m,int n)
:A(k),cc(m,n) //无标配时在子对象建立前初始化
{
cout << "B()" << endl;
cout << i << " " << j << endl;
}
private:
int b = 20;
C cc;
};
int main()
{
B b(3,2,1,4,5);
}
实战
#include<iostream>
using namespace std;
class Student
{
public:
Student(string n,char s,int c):name_(n),sex_(s),score_(c){}
void dis()
{
cout << name_ << " " << sex_ << " " << score_ << endl;
}
private:
string name_;
char sex_;
int score_;
};
class Birthday
{
public:
Birthday(int y,int m,int d)
:year_(y),month_(m),day_(d){}
void dumpbir()
{
cout << year_ << " " << month_ << " " << day_ << endl;
}
private:
int year_;
int month_;
int day_;
};
class Graduate :public Student
{
public:
Graduate(int s,string sn,char ss,int sc,int y,int m,int d)
:salary(s),Student(sn,ss,sc),bir(y,m,d) {}
void dump()
{
dis();
cout << salary << endl;
}
Birthday bir;
private:
int salary;
};
class Doctor:public Graduate
{
public:
Doctor(int s, string sn, char ss, int sc, int y, int m, int d, string st)
:Graduate( s, sn, ss, sc, y, m, d)
{
title = st;
}
void disdump()
{
dump();
cout << title << endl;
}
private:
string title;
};
int main()
{
Doctor d(10,"aa",'a',10,10,10,10,"aa");
d.disdump();
}
派生类的拷贝构造
引入:
拷贝构造也是一种构造,也没有被继承下来
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
}
A(const A& another)
{
cout << "A(const A& another)" << endl;
}
private:
int a;
};
class B :public A
{
public:
B()
{
cout << "B()" << endl;
}
B(const B& another)
{
cout<<"B(const B& another)" << endl;
}
private:
int b;
};
int main()
{
B b;
B bb(b);
}
终端输出:
A()
B()
A()
B(const B& another)
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
}
A(const A& another)
{
cout << "A(const A& another)" << endl;
}
private:
int a;
};
class B :public A
{
public:
B()
{
cout << "B()" << endl;
}
//B(const B& another)
//{
// cout<<"B(const B& another)" << endl;
//}
private:
int b;
};
int main()
{
B b;
B bb(b);
}
终端输出:
A()
B()
A(const A& another)
当子类不自实现时默认调用父类的拷贝构造。若子类自实现,且不作特殊处理,此时只会调用父类的构造器,此时失去了拷贝构造的意义
特殊处理( 显示的调用父类的拷贝构造)
#include<iostream>
using namespace std;
class A
{
public:
A(int x = 99)
{
a = x;
cout << "A()" << endl;
}
A(const A& another)
{
this->a = another.a;
cout << "A(const A& another)" << endl;
}
int a;
};
class B :public A
{
public:
B(int x,int y)
:A(x)
{
cout << "B()" << endl;
b = y;
}
B(const B& another)
:A(another)
{
cout<<"B(const B& another)" << endl;
this->b = another.b;
}
int b;
};
int main()
{
B b (199,200);
B bb(b);
//调用A的拷贝构造,再调用B的拷贝构造,在这一过程中将子类对象赋值给了父类的引用,也就是调用父类的构造器这一过程,称为赋值兼容
cout << bb.a << " " << bb.b << endl;
}
终端输出:
A()
B()
A(const A& another)
B(const B& another)
199 200
语法:
派生类::派生类(const 派生类& another)
:基类(another),派生类新成员(another.新成员)
{
//派生类新成员(another.新成员)
}
关于内嵌子对象:
#include<iostream>
using namespace std;
class C
{
public:
C()
{
cout << "C()" << endl;
}
C(const C& another)
{
cout << "C(const C& another)" << endl;
}
};
class A
{
public:
A(int x = 99) {
a = x;
cout << "A()" << endl;
}
A(const A& another)
{
this->a = another.a;
cout << "A(const A& another)" << endl;
}
int a;
};
class B :public A
{
public:
B(int x, int y)
:A(x)
{
cout << "B()" << endl;
b = y;
}
/*B(const B& another)
:A(another)
{
cout << "B(const B& another)" << endl;
this->b = another.b;
}*/
int b;
C cc;
};
int main()
{
B b(199, 200);
B bb(b);
}
终端输出:
A()
C()
B()
A(const A& another)
C(const C& another)
当子类不实现拷贝构造时,默认调用内嵌子对象的拷贝构造
#include<iostream>
using namespace std;
class C
{
public:
C()
{
cout << "C()" << endl;
}
C(const C& another)
{
cout << "C(const C& another)" << endl;
}
};
class A
{
public:
A(int x = 99) {
a = x;
cout << "A()" << endl;
}
A(const A& another)
{
this->a = another.a;
cout << "A(const A& another)" << endl;
}
int a;
};
class B :public A
{
public:
B(int x, int y)
:A(x)
{
cout << "B()" << endl;
b = y;
}
B(const B& another)
:A(another)
{
cout << "B(const B& another)" << endl;
this->b = another.b;
}
int b;
C cc;
};
int main()
{
B b(199, 200);
B bb(b);
}
终端输出:
A()
C()
B()
A(const A& another)
C()
B(const B& another)
若自实现且不做特殊处理(不显示调用),此时只会调用内嵌子对象的构造器(标配),此时拷贝构造无意义
那么就要显示调用:
#include<iostream>
using namespace std;
class C
{
public:
C()
{
cout << "C()" << endl;
}
C(const C& another)
{
cout << "C(const C& another)" << endl;
}
};
class A
{
public:
A(int x = 99) {
a = x;
cout << "A()" << endl;
}
A(const A& another)
{
this->a = another.a;
cout << "A(const A& another)" << endl;
}
int a;
};
class B :public A
{
public:
B(int x, int y)
:A(x)
{
cout << "B()" << endl;
b = y;
}
B(const B& another)
:A(another),cc(another.cc)
{
cout << "B(const B& another)" << endl;
this->b = another.b;
}
int b;
C cc;
};
int main()
{
B b(199, 200);
B bb(b);
}
终端输出:
A()
C()
B()
A(const A& another)
C(const C& another)
B(const B& another)
实战:
#include<iostream>
using namespace std;
class Student
{
public:
Student(string n = "aa", char s ='a', int c = 1) :name_(n), sex_(s), score_(c) {}
Student(const Student& another)
{
this->name_ = another.name_;
this->sex_ = another.sex_;
this->score_ = another.score_;
}
void dis()
{
cout << name_ << " " << sex_ << " " << score_ << endl;
}
private:
string name_;
char sex_;
int score_;
};
class Graduate :public Student
{
public:
Graduate(string sn, char ss, int sc,int s)
:salary(s), Student(sn, ss, sc) {}
Graduate(const Graduate& another)
:Student(another)
{
this->salary = another.salary;
}
void dump()
{
dis();
cout << salary << endl;
}
private:
int salary;
};
int main()
{
Graduate g("zhaosi", 's', 99, 1000);
Graduate gg(g);
g.dump();
gg.dump();
}
总结:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kev4U5nn-1655360070711)(E:\博客\photo\Snipaste_2022-05-29_16-43-21.png)]
派生类的赋值运算符重载
引入:
赋值运算符重载函数不是属于构造函数,所以可以继承
#include<iostream>
using namespace std;
class A
{
public:
A(int x)
{
a = x;
cout << "A()" << endl;
}
A& operator=(const A& another)
{
cout << "A& operator=(const A& another)" << endl;
this->a = another.a;
return *this;
}
int a;
};
class B :public A
{
public:
B(int x,int y)
:A(x),b(y)
{
cout << "B()" << endl;
}
B& operator=(const B& another)
{
cout << "B& operator=(const B& another)" << endl;
this->b = another.b;
return *this;
}
int b;
};
int main()
{
B b(1,2);
B bb(3,4);
b = bb;
cout << b.a << " " << b.b << endl;
cout << bb.a << " " << bb.b << endl;
}
终端输出:
A()
B()
A()
B()
B& operator=(const B& another)
1 4
3 4
只调用B的=
#include<iostream>
using namespace std;
class A
{
public:
A(int x)
{
a = x;
cout << "A()" << endl;
}
A& operator=(const A& another)
{
cout << "A& operator=(const A& another)" << endl;
this->a = another.a;
return *this;
}
int a;
};
class B :public A
{
public:
B(int x,int y)
:A(x),b(y)
{
cout << "B()" << endl;
}
//B& operator=(const B& another)
//{
// cout << "B& operator=(const B& another)" << endl;
// this->b = another.b;
// return *this;
//}
int b;
};
int main()
{
B b(1,2);
B bb(3,4);
b = bb;
cout << b.a << " " << b.b << endl;
cout << bb.a << " " << bb.b << endl;
}
终端输出:
A()
B()
A()
B()
A& operator=(const A& another)
3 4
3 4
调用了B的默认=
做出改进:
#include<iostream>
using namespace std;
class A
{
public:
A(int x)
{
a = x;
cout << "A()" << endl;
}
A& operator=(const A& another)
{
cout << "A& operator=(const A& another)" << endl;
this->a = another.a;
return *this;
}
int a;
};
class B :public A
{
public:
B(int x,int y)
:A(x),b(y)
{
cout << "B()" << endl;
}
B& operator=(const B& another)
{
A::operator=(another);
cout << "B& operator=(const B& another)" << endl;
this->b = another.b;
return *this;
}
int b;
};
int main()
{
B b(1,2);
B bb(3,4);
b = bb;
cout << b.a << " " << b.b << endl;
cout << bb.a << " " << bb.b << endl;
}
终端输出:
A()
B()
A()
B()
A& operator=(const A& another)
B& operator=(const B& another)
3 4
3 4
解释:
经过第一次的试探,我们知道再子类中自实现赋值运算符重载之后,此类对象之间在进行 = 运算时只会调用该类中的运算符重载,而不去调用父类的运算符重载,又知道运算符重载不属于构造函数,会被继承到子类中,因此想到在子类中调用父类的运算符重载函数,从而达到父类与子类赋值重载都被调用的目的。
A::operator=(another); 这里在调用父类运算符重载时加A: :的原因,如果不加作用域A,编译器就会认为operator=(another)就是调用本作用域的函数,巧的是这个函数就是这个函数体本身,从而形成了递归调用陷入死循环
关于内含子对象
#include<iostream>
using namespace std;
class C
{
public:
C(int x)
{
cout << "C()" << endl;
}
C& operator=(const C& another)
{
cout << "C& operator=(const C& another)" << endl;
this->c = another.c;
return *this;
}
int c;
};
class A
{
public:
A(int x)
{
a = x;
cout << "A()" << endl;
}
A& operator=(const A& another)
{
cout << "A& operator=(const A& another)" << endl;
this->a = another.a;
return *this;
}
int a;
};
class B :public A
{
public:
B(int x,int y)
:A(x),b(y),c(x)
{
cout << "B()" << endl;
}
B& operator=(const B& another)
{
A::operator=(another);
cout << "B& operator=(const B& another)" << endl;
this->b = another.b;
c = another.c; //这里调用类C的赋值重载函数
return *this;
}
int b;
C c;
};
int main()
{
B b(1,2);
B bb(3,4);
b = bb;
cout << b.a << " " << b.b << endl;
cout << bb.a << " " << bb.b << endl;
}
终端输出:
A()
C()
B()
A()
C()
B()
A& operator=(const A& another)
B& operator=(const B& another)
C& operator=(const C& another)
3 4
3 4
解释:
c = another.c这里,c是对象bb的成员,another.c是对象b的成员,这两个成员都是类C的对象,因此再进行赋值运算的时候就会调用 类C 的赋值运算符的重载函数,从而达到目的
总结:
- 子类中若未实现赋值重载,调用父类赋值重载(深或浅)。
- 子类中 实现赋值重载,若无显示的调用父类的赋值重载,父类成员 的保持己有构造(构造函数)。此时将失去赋值的意义,通常要显示的调用父类的赋值重载
- 子类中若未实现赋值重载,调用内嵌子对象赋值重载
- 子类中实现赋值重载,若无显示的调用内嵌子对象赋值重载,则内 嵌子对象保持己有构造。此时将失去赋值的意义,通常要显示的调用内嵌子对象赋值重载
- 显示的调用父类的赋值重载,需要加父类和域作用域运算符,避免死递归。
实战
#include<iostream>
using namespace std;
class Brithday
{
public:
Brithday(int y, int m, int d)
:year(y),month(m),day(d){}
Brithday& operator=(const Brithday& another)
{
this->day = another.day;
this->month = another.month;
this->day = another.day;
return *this;
}
private:
int year; int month; int day;
};
class Student
{
public:
Student(string n, char s, int c) :name_(n), sex_(s), score_(c) {}
void dis()
{
cout << name_ << " " << sex_ << " " << score_ << endl;
}
Student& operator=(const Student& another)
{
this->name_ = another.name_;
this->score_ = another.score_;
this->sex_ = another.sex_;
return *this;
}
private:
string name_;
char sex_;
int score_;
};
class Graduate :public Student
{
public:
Graduate(int s, string sn, char ss, int sc,int y,int d,int m)
:salary(s), Student(sn, ss, sc),bir(y,m,d) {}
void dump()
{
dis();
cout << salary << endl;
}
Graduate& operator=(const Graduate& another)
{
bir = another.bir;
Student::operator=(another); //赋值运算符函数被继承要显式调用
this->salary = another.salary; //显示调用内嵌子对象的复制重载函数
return *this;
}
private:
int salary;
Brithday bir;
};
int main()
{
Graduate g(10, "aa", 'a', 100,2002,3,23);
g.dump();
Graduate gg = g;
gg.dump();
}
派生类的友元函数
由于友元函数并非类成员,因引不能被继承(相当于我爹的朋友不一定是我的朋友),在某种需求下,可能希望派生类的友元 函数能够使用基类中的友元函数。为此可以通过强制类型转换,将派生类的指针或是引用 强转为其父类的引用或是指针,然后使用转换后的引用或是指针来调用基类中的友元函数
#include<iostream>
using namespace std;
class Student
{
friend ostream& operator<<(ostream& out, Student& another);
public:
Student(int x,int y)
{
a = x; b = y;
}
private:
int a;
int b;
};
class Graduate :public Student
{
friend ostream& operator<<(ostream& out, Graduate& gra);
public:
Graduate(int x,int y,int z)
:Student(x,y)
{
c = z;
}
private:
int c;
};
ostream& operator<<(ostream& out, Student& another)
{
cout << another.a << endl;
cout << another.b << endl;
return out;
}
ostream& operator<<(ostream& out, Graduate& gra)
{
cout << (Student&)gra; //强转为Student类的引用
cout << gra.c << endl;
return out;
}
int main()
{
Graduate g(1,2,3);
cout << g << endl;
return 0;
}
今天学完之后:不理解 赋值兼容
day8
继承方式
继承后的子类对父类成员的权限:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bPfN3L5L-1655360070712)(E:\博客\photo\Snipaste_2022-05-30_22-23-34.png)]
类的作用域运算之shadow(隐藏)
如果某派生类的基类拥有同名的成员,同时,派生类又新增这样的同名成员,在 这种情况下,派生类成员将 shadow(隐藏)所有基类的同名成员。这时就需要基类名+作用 域运算符的方式才能调用基类的同名成员。shadow的条件就一个,就是父类与子类的标识符重名,父类的标识符就被隐藏 ,直接调用只能调用子类的
#include <iostream>
using namespace std;
class Base
{
public:
void func()
{
cout<<"haha"<<endl;
}
};
class Drive:public Base
{
public:
void func()
{
//func(); //this->func() 死循环
//Base::func(); //被 shadow 的成员,可以这样访问
cout<<"hehe"<<endl;
}
};
int main()
{
Drive d;
d.func(); // 访问派生类成员 ,默认使用d的地址做this指针
//d.Base::func(); //访问基类成员
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uImNN1U7-1655360070712)(E:\博客\photo\Snipaste_2022-05-31_11-06-03.png)]
why public?
#include <iostream>
using namespace std;
class Base
{
public:
int pub;
protected:
int pro;
private:
int pri;
};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BuPEzSQQ-1655360070713)(E:\博客\photo\Snipaste_2022-05-31_11-21-00.png)]
如果多级派生当中,均采用 public,直到最后一级,派生类中均可访问基类的 public,protected 成员
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xnf9YWaZ-1655360070716)(E:\博客\photo\Snipaste_2022-05-31_11-23-29.png)]
多级派生当中,均采用 protected,直到最后一级,派生类的基类的所有成员即使 可见,也均不可被类外调用。
多继承
继承语法:
派生类名:public 基类名 1,public 基类名 2,...,protected 基
构造格式:
派生类名::派生类名(总参列表) :基类名 1(参数表 1),基类名(参数名 2)....基类名 n(参数名 n),
内嵌子对象 1(参数表 1),内嵌子对象 2(参数表 2)...内嵌子对象 n(参数表 n)
{
派生类新增成员的初始化语句;
}
例子:沙发床
class Sofa
{
public:
void sit()
{cout << "take a sit have rest" << endl;}
};
class Bed
{
public:
void sleep()
{cout << "have sleep" << endl;}
};
class SofaBed :public Sofa, public Bed
{};
int main()
{
SofaBed sb;
sb.sit(); sb.sleep();
}
三角问题:
多个父类中重名的成员,继承到子类中后,为了避免冲突,携带了各父类的作用域信 息,子类中要访问继承下来的重名成员,则会产生二义性,为了避免冲突,访问时需要提 供父类的作用域信息。
#include <iostream>
using namespace std;
class X
{
public:
X(int d)
:_data(d) {}
void setData(int i)
{
_data = i;
}
int _data;
};
class Y
{
public:
Y(int d)
:_data(d) {}
int getData()
{
return _data;
}
int _data;
};
class Z :public X, public Y
{
public:
Z() :X(2), Y(3)
{}
void dis()
{
cout << X::_data << endl;
cout << Y::_data << endl;
}
};
int main()
{
Z z;
z.dis();
z.setData(2000); //这里将父类x里的data成员的数据设成了2000
cout << z.getData() << endl; //这里输出的是类Y内的数据成员data的值
return 0;
}
终端输出:
2
3
3
下面进行改进:
三角关系中需要解决的问题有两类: 数据冗余问题, 访问不方便的问题。
解决方案,是三角转四角的问题。具体操作: 提取公共成员构成祖父类,即虚基类, 各父类虚继承虚基类。
1 提取公因子:
#include <iostream>
using namespace std;
class A
{
public:
A(int i)
:_data(i){}
int _data;
};
class X:public A
{
public:
X(int d)
:A(d){}
void setData(int i)
{
_data = i;
}
};
class Y:public A
{
public:
Y(int d)
:A(d){}
int getData()
{
return _data;
}
};
class Z:public X,public Y
{
public:
Z():X(2),Y(3)
{}
void dis()
{
cout<<X::_data<<endl;
cout<<Y::_data<<endl;
}
};
int main()
{
Z z;
z.dis();
z.setData(2000);
cout<<z.getData()<<endl;
return 0;
}
终端输出:
2
2
3
2 虚继承
#include <iostream>
using namespace std;
class A
{
public:
A(int i)
:_data(i){}
int _data;
};
class X: virtual public A /
{
public:
X(int d)
:A(d){}
void setData(int i)
{
_data = i;
}
};
class Y:public virtual A //虚继承
{
public:
Y(int d)
:A(d){}
int getData()
{
return _data;
}
};
class Z:public X,public Y
{
public:
Z():X(2),Y(3),A(100)
{}
void dis()
{
cout<<X::_data<<endl;
cout<<Y::_data<<endl;
}
};
int main()
{
Z z;
z.dis();
z.setData(2000);
cout<<z.getData()<<endl;
return 0;
}
关于虚继承
意义:
在多继承中,保存共同基类的多份同名成员,虽然有时是必要的,可以在不同的数据 成员中分别存放不同的数据,但在大多数情况下,是我们不希望出现的。 因为保留多份数据成员的拷贝,不仅占有较多的存储空间,还增加了访问的困难。为 此,C++提供了,虚基类和虚继承机制,实现了在多继承中只保留一份共同成员
class 父类:virtual public 虚基类
虚基类:
经提取,存有公共元素的,被虚继承的祖父类,称为虚基类。虚基类,需要设计和抽 象,虚继承,是一种继承的扩展
初始化顺序:
#include<iostream>
using namespace std;
class A
{
public:
A(int i)
{
cout << "A(int i)" << endl;
}
protected:
int data_;
};
class B :virtual public A
{
public:
B(int i)
:A(i)
{
cout << "B(int i)" << endl;
data_ = i;
}
};
class C : virtual public A
{
public:
C(int i)
:A(i)
{
cout << "C(int i)" << endl;
data_ = i;
}
};
class D :public B, public C //初始化父类的顺序为先 B 再 A
{
public:
D()
:B(1),C(1),A(1) //有默认系统隐式调用,没有就只能显示调用,此处加A(1)的原理我也不知道,但是就必须加,不交就报错
{
cout << "D(int i)" << endl;
}
void dis()
{
cout << data_<< endl;
}
};
int main()
{
D d;
}
终端输出:
A(int i)
B(int i)
C(int i)
D(int i)
先祖父类,再父类(按照再子类中声明继承关系的顺序,从左到右),最后是子类
多态
C++中所谓的多态(polymorphism)是指,由继承而产生的相关的不同的类,其对象对 同一消息会作出不同的响应。 比如,mspaint 中的单击不同图形,执行同一个拖动动作而绘制不同的图形,就是典 型的多态应用。 多态性是面向对象程序设计的一个重要特征,能增加程序的灵活性。可以减轻系统升 级,维护,调试的工作量和复杂度
赋值兼容
赋值兼容的规则
赋值兼容规则是指,在需要基类对象的任何地方,都可以使用公有派生类的对象来替 代。 只有在公有派生类中才有赋值兼容,赋值兼容是一种默认行为,不需要任何的显示的 转化步骤。
赋值兼容的细化:
- 派生类的对象可以赋值给基类对象。
- 派生类的对象可以初始化基类的引用。
- 派生类对象的地址可以赋给指向基类的指针
实例:
派生类的对象可以赋值给基类对象。
class Shape
{
public:
Shape(int x = 0, int y = 0)
:x_(x), y_(y) {}
void draw()
{
cout << "draw shape from (" << x_ << "," << y_ << ")"<<endl;
}
protected:
int x_;
int y_;
};
class Rect:public Shape
{
};
class Circle :public Shape
{
public:
Circle(int x = 0,int y = 0,int r = 0)
:Shape(x,y),r_(r){}
void draw()
{
cout << "draw circle from (" << x_ << "," << y_ << ")" <<"r = "<<r_ << endl;
}
protected:
int r_;
};
int main()
{
Shape s(1,2);
Circle c(3,4);
s.draw(); c.draw();
s = c;
s.draw();
}
终端输出:
draw shape from (1,2)
draw circle from (3,4,5)r = 5
draw shape from (3,4)
派生类的对象可以初始化基类的引用
int main()
{
Circle c(4, 5, 6);
Shape& rs = c;
rs.draw();
}
终端输出:
draw shape from (4,5)
派生类对象的地址可以赋给指向基类的指针
int main()
{
Circle c(4, 5, 6);
Shape* ps = &c;
ps->draw();
}
终端输出:
draw shape from (4,5)
多态形成的条件
静多态:
静多态也就是我们说的函数重载。表面上看,是由重载规则决定的,内部实现却是命名倾轧,这种行为发生在编译阶段,故称为静多态。
多态:
(动)多态不是在编译阶段决定的,而是在运行阶段决定的。动多态形成的条件:
- 父类中有虚函数. //加 virtual 关键字
- 子类override(覆写)父类的虚函数 //子类中同名同参同返回
- 通过被子类对象赋值的父类指针调用共用接口 //调用虚函数形成多态
若不是虚函数:
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
cout << "A:foo:" << endl;
}
};
class B:public A
{
public:
void foo() //首先这里构成了shadow,父类foo函数被隐藏
{
cout << "B:foo:"<<endl;
}
protected:
};
int main()
{
B b;
A* pf = &b; // 赋值兼容,将子类的地址赋给了父类的指针
pf->foo(); //调用父类成员,此时pf相当于this指针
}
终端输出:A:foo:
变为虚函数:
#include<iostream>
using namespace std;
//virtual 生明星关键字,用于声明函数为虚函数。子类中用于覆写的函数也是(virtual)虚函数
//覆写:同名同参同返回
class A
{
public:
virtual void foo()
{
cout << "A:foo:" << endl;
}
};
class B:public A
{
public:
virtual void foo() //与父类中函数同名同参同返回,用于覆写
{
cout << "B:foo:"<<endl;
}
};
int main()
{
B b;
A* pf = &b; //赋值兼容
pf->foo(); //由于是虚函数且满足覆写再将子类对象的地址赋值给父类对象的指针,从而使得父类指针调用子类虚函数
}
终端输出: B::foo
例子:
#include<iostream>
using namespace std;
class Shape
{
public:
Shape(int x = 0, int y = 0)
:x_(x), y_(y) {}
virtual void draw()
{
cout << "draw shape from (" << x_ << "," << y_ << ")" << endl;
}
protected:
int x_;
int y_;
};
class Rect :public Shape
{
public:
Rect(int x = 0,int y = 0,int w = 0,int l = 0)
:Shape(x,y),width_(w),lengh_(l){}
virtual void draw()
{
cout << "draw Rect from (" << x_ << "," << y_ << ")" << "w = " << width_ <<" l = "<<lengh_<< endl;
}
protected :
int width_;
int lengh_;
};
class Circle :public Shape
{
public:
Circle(int x = 0, int y = 0, int r = 0)
:Shape(x, y), r_(r) {}
virtual void draw()
{
cout << "draw circle from (" << x_ << "," << y_ << ")" << "r = " << r_ << endl;
}
protected:
int r_;
};
int main()
{
Circle c(1, 2, 3);
Shape* ps = &c;
ps->draw();
Rect r(7, 8, 9, 0);
ps = &r;
ps->draw();
}
override关键字
override 是 C++11 中引入的关键字,用于修饰覆写的虚函数。表明父类中有此虚函数, 同名,同参,同返回中有一样不满足,则会报错。 这样的好处是,假设在覆写中,写错了,比如,作不到同名,同参,同返中的任一样, 编译器都会给出提示
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw(){}
};
class Rect:public Shape
{
public:
// void drawx() override
// {}
// void draw(int a) override
// {}
// int draw() override
// {}
// void draw() const override //只有在同名同参同返回暂时不报错
// {}
void draw() override
{}
};
int main(){
return 0;
}
权限与访问的关系
#include<iostream>
using namespace std;
class A
{
public:
virtual void foo() //这里必须是public
{
cout << "A:foo:" << endl;
}
};
class B :public A
{
private: //这里public protected public都可以
virtual void foo()
{
cout << "B:foo:" << endl;
}
int a = 100;
};
int main()
{
B b;
A* pf = &b;
f->foo(); //这里去调用了父类中的foo函数,再去调用子类中的foo函数
}
子类中覆写的函数可以是任意访问类型,根据子类需求而定。
纯虚数和虚析构
纯虚函数
纯虚函数,是一种虚函数,virutal 修饰,没有实现体,且被"初始化"为零的函数。纯虚函数存在的意义在于,提供类接口使用。含有纯虚函数的类称为抽象基类。抽象基类不能实例化,即,不可产生对象
格式:
class 类名
{
virtual 函数声明 = 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8KqD5awb-1655360070717)(E:\博客\photo\Snipaste_2022-06-03_11-04-42.png)]
虚析构函数
含有虚函数的类,析构函数也应该声明为虚函数,称为虚析构函数。在 delete 父类指针指向的堆上子类对象时,会调用子类的析构函数,实现完整析构。
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal()" << endl;
}
~Animal()
{
cout << "~Animal()" << endl;
}
virtual void voice() = 0;
};
class Dog:public Animal
{
public:
Dog()
{
cout << "Dog()" << endl;
}
~Dog()
{
cout << "~Dog" << endl;
}
virtual void voice()
{
cout << "wang wang wang" << endl;
}
};
int main()
{
Animal* pf = new Dog;
pf->voice();
delete(pf);
}
终端输出:
Animal()
Dog()
wang wang wang
~Animal()
在使用普通的析构函数时,观察到只析构了父类,而没有析构子类,这并不完美,所以引入虚析构函数
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal()" << endl;
}
virtual ~Animal()
{
cout << "~Animal()" << endl;
}
virtual void voice() = 0;
};
class Dog:public Animal
{
public:
Dog()
{
cout << "Dog()" << endl;
}
virtual ~Dog()
{
cout << "~Dog" << endl;
}
virtual void voice()
{
cout << "wang wang wang" << endl;
}
};
int main()
{
Animal* pf = new Dog;
pf->voice();
delete(pf);
}
终端输出:
Animal()
Dog()
wang wang wang
~Dog
~Animal()
这样使得析构完整.
结论:
但凡类中有虚函数(包括纯虚函数),都将析构函数变为virtual,为了完整析构