类的学习总结
类分为封装,继承,多态
类的封装性:类防止像结构体一样被外部修改所以类就有封装性,默认的class如果没有声明的话一般就是private的这样的数据成员只能用该类里面的成员函数进行访问,public的成员可以在类外进行直接访问没有安全性,protect成员只能是该类能对这种数据成员进行访问,在别的类里面无法进行访问同样需要调用函数,还有就是类里面的函数一定要有返回值,否则没法改动原来类成员的值。A类作为B类的成员函数组合,相对应的在B类要写一个getA函数且带返回值(一定不要忘了),当把一个类放入一个向量里面class B vectora
这样别忘了里面写get向量的函数getA(int i){return a[i]}才可以对向量进行访问和对vector中的函数进行访问,还有构造函数,当类生成时给类的成员进行初始化,析构函数当类消亡的时候在执行析构函数的函数。总而言之一定要弄清楚类的公有与私有的各个关系。
类的继承:
概念:继承的类分为基类与派生类,比如说一个类是学生类还有一个研究生类以及本科生类,本科生类和研究生类同时可以继承学生这个类学生类里面的元素只要是个学生就具备,而继承他的派生类额外有自己的独特的元素就是在基类的基础上增加。
继承的类型:
公有继承和私有继承。公有继承原本基类里面的公有成员还是公用的,保护的成员还是保护的,私有成员还是不能被直接调用的,私有继承,原来基类的共有元素和保护元素成为派生类的私有成员,基类的私有成员还是不能直接调用,还是属于基类得私有成员。
改变个别成员的可访问性:
class Base
{
public:
void func();
private:
void area();
protect:
void get();
}
class Derve:public Base
{
protect:
using Base::func;//把继承过来的公有的函数声明成派生类的保护元素;
public:
using Base::get;//把继承过来的protect成员声明成public类型
private://这里只能对继承过来的可以直接使用的成员进行重新声明
}
这个东西就是个别的自定义的继承,比整个私有继承或是公有继承范围小;
在派生类中使用基类成员:
对于public直接继承的基类的成员除了基类的private成员不能直接使用需要靠调用基类的访问私有成员的函数进行访问之外,其他的基类成员可以直接进行访问。例如:
class A
{ public :
void get_XY() { cout << "Enter two numbers of x, y : " ; cin >> x >> y ; }
void put_XY() { cout << "x = "<< x << ", y = " << y << '\n' ; }
protected: int x, y ;
};
class B : public A
{ public :
int get_S() { return s ; };
void make_S() { s = x * y ; }; // 使用基类数据成员x,y
protected: int s;
};
对于调用基类成员里的私有成员:
#include<iostream>
#include <string>
using namespace std;
class Person
{
string name;//基类的私有成员;
int age;
string sex;
public:
void set_p() {
cout<<"name\tage\tsex"<<endl;
cin>>name>>age>>sex;
}//私有成员访问的函数;
void show_p() {
cout<<name<<" "<<age<<" "<<sex<<endl;
}
};
class student :public Person
{
string no;
string zhuanye;
string t_class;
float score;
public:
void set_t(){
set_p(); //调用继承于基类的成员函数访问继承于基类的私有数据成员
cin>>zhuanye>>t_class>>score;
/*若这里派生类的函数名跟基类函数名一样那么基类的函数就被隐藏了;
void set(){ //隐藏了基类中的同名成员
Person::set(); //调用继承于基类的成员函数访问继承于基类的数据成员
*/
}
void show_t() {
show_p();
cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;
}
};
同理若基类成员与派生类成员重名,
class base
{ public :
int a , b ;
} ;
class derived : public base
{ public :
int b , c ;
} ;
void f ()
{ derived d ;
d . a = 1 ;
d . base :: b = 2 ;//基类的成员
d . b = 3 ;
d . c = 4 ;
};可以这样来处理
同样的出现了同名函数
class B : public A
{ public:
int b1, b2 ;
B( int j1=1, int j2=1 ) { b1 = j1; b2 = j2; }
void print() //定义同名函数
void printAB()
{ A::print() ; //派生类对象调用基类版本同名成员函数
print() ; //派生类对象调用自身的成员函数
}
};
int main()
{ B b ; b.A::print(); b.printAB(); }
其实这以上这些操作不是很需要使用没有人会在使用的时候故意找这么多麻烦;
静态成员的调用:类名 :: 成员
class parent_class
{ int data1 , data2 ;
public :
parent_class ( int p1 , int p2 ) { data1 = p1; data2 = p2; }//基类构造函数
int inc1 () { return ++ data1; }
int inc2 () { return ++ data2 ; }
void display () {cout << "data1=" << data1 << " , data2=" << data2 << endl ; }
};
class derived_class : private parent_class
{ int data3 ;
parent_class data4 ;
public:
derived_class ( int p1 , int p2 , int p3 , int p4 , int p5 ): parent_class ( p1 , p2 ) /*基类构造函数*/, data4 ( p3 , p4 )一个基类的对象
{ data3 = p5 ; }//派生类的初始
int inc1 ( ) { return parent_class :: inc1 ( ) ; }
int inc3 ( ) { return ++ data3 ; }
void display ( )
{ parent_class :: display ( ) ; data4.display ( ) ;
cout << "data3=" << data3 << endl ;
}
} ;
派生类::派生类名(参数总表):基类名(参数表)
{
// 派生类新增成员的初始化语句
}
构造函数与析构函数的执行情况:
在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数→基类的析构函数。
(2)当派生类中含有对象成员时
在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数。
避免一个类继承多个类c++这方面可能会出现错误;
类的多态:
虚函数与基类指针:
基类指针能获取派生类的地址,但是只能执行访问的操作,通过基类指针对派生类进行赋值等操作。
class Base
{ public : Base(char xx) { x = xx; }
void who() { cout << "Base class: " << x << "\n" ; }
protected: char x;
} ;
class First_d : public Base
{ public : First_d(char xx, char yy):Base(xx) { y = yy; }
void who() { cout << "First derived class: "<< x << ", " << y << "\n" ; }
protected: char y;
} ;
class Second_d : public First_d
{ public :
Second_d( char xx, char yy, char zz ) : First_d( xx, yy ) { z = zz; }
void who() { cout << "Second derived class: "<< x << ", " << y << ", " << z << "\n" ; }
protected: char z;
} ;
int main()
{ Base B_obj( 'A' ) ; First_d F_obj( 'T', 'O' ) ; Second_d S_obj( 'E', 'N', 'D' ) ;
Base * p ;
p = & B_obj ; p -> who() ;
p = &F_obj ; p -> who() ;
p = &S_obj ; p -> who() ;
F_obj.who() ;
( ( Second_d * ) p ) -> who() ;
}
简单的访问的操作;
虚函数的重载特性:
构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数
析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象
class A
{ public:
~A(){ cout << "A::~A() is called.\n" ; }
} ;
class B : public A
{ public:
~B(){ cout << "B::~B() is called.\n" ; }
} ;
int main()
{ A *Ap = new B ;
B *Bp2 = new B ;
cout << "delete first object:\n" ;
delete Ap;
cout << "delete second object:\n" ;
delete Bp2 ;
}
输出结果是:
delete first object
A::~A() is called
delete second object
B::~B() is called
A::~A() is called
如果将A类中的析构函数换为virtual ~A(){ cout << “A::~A() is called.\n” ; }
则输出的第一个析构函数就是
B::~B() is called
A::~A() is called
因为基类被继承的时候虚函数会被隐藏,后来基类消失的时候也会先调用b的析构函数在调用a的析构函数。
成员函数调用虚函数:
class A
{ public:
virtual double funA(double x)
{ cout<<"funA of class A called."<<endl;
return x*x; }
double funB(double x)
{ return funA(x)/2; }
};
class B:public A
{ public:
virtual double funA(double x)
{ cout<<"funA of class B called."<<endl;
return 2*x*x; }
};
class C:public B
{ public:
virtual double funA(double x)
{ cout<<"funA of class C called."<<endl;
return 3*x*x;
}
};
void main()
{
C objc;
cout<<objc.funB(3)<<endl;
B objb;
cout<<objb.funB(3)<<endl;
}
输出为:funA of class C called.
13.5
funA of class B called.
9
也就是调用基类的函数的时候总是会先调用派生类里的虚函数,在调用基类里的函数,但他不会先输出派生类里的虚函数,只会最后在输出基类里的函数。
纯虚函数与抽象类:
一个具有纯虚函数的基类是抽象类,抽象类不能建立对象,也不能作为函数返回的类型,传值参数类型的函数也不行。可以声明指针,可以引用。
纯虚函数说明形式:
virtual 类型 函数名(参数表)= 0
在基类中声明在派生类中定义