c++类

将c++的类的相关知识做些总结

 

面向对象:

1.数据抽象

2.数据封装

3.模块化

4.软件复用

5.软件维护

 

类中的数据成员应在构造函数内初始化。

通常将类定义存放到.h文件中,在cpp中实现成员函数的实现。

成员函数可以在类内实现,也可以在类外实现,在类外实现:

void A::display(){}

在类中实现的成员函数是建议编译器将成员函数作为内联函数编译,内联函数是指编译器将该函数展开到调用点,以减少函数调用的时间,所以内联函数是一些较为短小的函数。

若数据成员是类类型,而且这个类没有定义或者没有定义完,那么该数据成员只能是指针或引用类型。

 

访问控制:

public:程序中任何位置均可访问

private:本类和友元中访问,默认访问控制

protected:本类,友元,派生类中可访问

union和struct的默认访问控制是public

 

new 和 malloc的区别:

new自动分配足够大小的空间,malloc需指定

new自动调用类的默认构造函数,malloc不会,free也不会自动调用析构函数

new返回指定类型的指针,malloc返回void指针

new可以重载

 

对于每一个类的对象,对于非静态数据成员,有各自的内存空间,每一个类内实现的成员函数,第一个参数默认为this,指向调用该函数的对象,this的类型为:

<A> *const this;

void f(A *p);

class A{

public:

  void func(){   f(this);  }

};

 

构造函数和析构函数

构造函数用于对象的初始化,析构函数用于对象消亡前的处理。

默认构造函数,重载的构造函数,拷贝构造函数

对于常量数据成员和引用数据成员,可以在定义构造函数时使用成员初始化表初始化他们。

class A{

private:

  int x;

  const int y ;

  int &z ;

public:

  A():z(x),y(1){

  x = 1;

  }

};

初始化顺序由数据成员定义时顺序决定。

若不使用成员初始化表初始化常量和引用数据,那么编译器不会提供默认的构造函数,因为提供的默认构造函数无法对常量和引用数据初始化,所以该类无法创建对象。

 

若类中存在其他类的对象,那么,构造函数调用顺序是:

先调用自身的构造函数,但在执行之前,将先调用成员初始化表中的指明的成员对象类的构造函数,若没有成员初始化表那么调用默认构造函数,然后再执行自身的构造函数。

析构函数则是先调用自身的析构函数,执行完后在调用成员对象类的析构函数。

如果有多个成员对象,那么构造函数的调用依据成员对象在类中的定义顺序来。析构函数则相反。

 

拷贝构造函数:

 A(const <A>&);

1. A a1;

    A a2(a1);

2. void f(A x);

    A a;

    f(a);

3.A f(){

    A a;

    ...

   return a;

   }

编译器提供一个隐式的拷贝构造函数,该函数对数据成员采用常规的初始化操作,对于成员对象,则调用成员对象类的拷贝构造函数。

隐式的拷贝构造函数通过=操作初始化一般的数据成员,所以会导致两指针指向同一块区域:一个指针的操作会影响到另一个指针,调用析构函数消亡时,重复释放同一块内存,产生异常。

所以要显式定义一个拷贝构造函数,但是显式的拷贝构造函数不会自动调用成员对象类的拷贝构造函数,可以使用成员初始化表中显式调用。

 

对于常成员函数:

类的常量对象只能调用常成员函数。

 

常成员函数在函数体内不能修改数据成员的值

void f()const {  } 无论定义还是声明,都需要加const

为了防止函数修改形参对象,通常将形参定义为常量对象指针或常量对象指针:

void f ( const  A *pa);

void f ( const A &pa);

 

静态数据成员:

所有对象共享的数据成员

static ...

静态数据成员只能在类外定义,静态数据成员也要遵循访问控制规则。

class A {

  static int s;

  .....

}

int A::s = 0; 

 

静态成员函数:

static void f(){  }

静态成员函数只能访问静态成员(数据成员和静态函数).

可以通过对象访问静态成员也可以通过类名受限访问: A::f();

 

友元:

友元可以提高对象对private和protected成员的访问效率.但也和数据封装的原则相冲突,友元是两者的折中方案。

友元:友元函数,友元类,友元类成员函数

friend f();

friend class B;

friend void C::func();

 

操作符重载:

<return type> operator <操作符>( ){ }

.(成员选择符)  .*(间接成员选择符)  ::(域解析符)  ?:  sizeof()  这五个操作符无法重载. 

 

派生类:

定义派生类前,需要先定义基类。

派生类中可以使用基类的public和protected成员。

派生类可以定义与基类中同名的函数,派生类的函数将隐藏基类函数,可以使用基类名受限访问基类的函数。

 

继承方式:

public

private

protected

继承方式的调整:

class A{

public:

  void f();

protected:

  void g();

};

class B:private A{

public:

  A::f;

protected:

  A::g;

}; 

 

派生类对象的初始化和赋值操作

派生类对象的初始化调用自身的构造函数,在执行前,先根据成员初始化表调用基类的相应的构造函数,若没有显式指出,则默认调用基类的默认构造函数。然后根据成员初始化表调用成员对象类的构造函数,若没有显式指出,则调用默认构造函数,最后调用派生类的构造函数。析构函数则相反。

对于拷贝构造函数,派生类的隐式拷贝函数将会调用基类的拷贝构造函数,而派生类的自定义构造函数默认调用基类的默认构造函数,可以在自定义拷贝函数的成员初始化表中显示的指出。

 

成员函数调用的动态绑定

c++默认采用静态绑定,在编译时刻根据对象的类型调用函数。

但是可以将函数声明为虚函数,在其派生类中定义一个同名,参数相同,返回类型相同的函数覆盖基类函数。若类对象是引用或指针类型则对该函数采用动态绑定。

通过类名受限访问虚函数不采用动态绑定

 

虚函数:

只有类的成员函数才是虚函数

静态成员函数不能使虚函数

构造函数不能是虚函数

析构函数可以是虚函数

 

纯虚函数和抽象类

纯虚函数是指给出声明而没有定义的徐成员函数。

virtual int f()=0;

包含纯虚函数的类称为抽象类。抽象类不能用于创建对象。其主要作用在于为派生类提供一个基本框架和一个公共对外接口,派生类需要对抽象基类的所有纯虚函数进行实现。

 

多继承

对基类的声明次序决定了对基类的构造函数和析构函数的调用顺序,决定了数据成员的存储顺序。

名冲突:使用基类名受限访问

 

重复继承--虚基类

class A;

class B:public A{};

class C:public A{};

class D:public B,public C{};

解决方案是将A定义为B,C的虚基类。

 

对于虚基类

虚基类的构造函数由最新派生出来的类的构造函数调用

虚基类的构造函数有限非虚基类的构造函数执行 

  

一个例子:关于对象的构造和析构的调用次序:

#include<iostream>
using namespace std;

class A{
	int x,y;
	const int ci;
	char *c;
	static int count;
public:
	A():ci(1){
		cout<<"A:init1"<<endl;
		x=y=0;
		c = new char[5];
		strcpy(c,"zcx");
		count++;
	}
	A(int x1,int y1,char *c1):ci(1){
		cout<<"A:init2"<<endl;
		x=x1;
		y=y1;
		c = new char[strlen(c1)+1];
		strcpy(c,c1);
		count++;
	}
	A(A const &a):ci(1){
		cout<<"A:init3"<<endl;
		x=a.x;
		y=a.y;
		c = new char[strlen(a.c)+1];
		strcpy(c,a.c);
		count++;
	}

	void show()const;

	virtual void display(){cout<<"A:display"<<endl;}
	~A(){
		cout<<"A:delete"<<endl;
		delete c;
		c=NULL;
	}
};
int A::count = 0;

void A::show()const{
	cout<<x<<' '<<y<<' '<<c<<' '<<count<<endl;
}

class B:public A{
	int z;
	A a;
public:
	B():a(1,1,"a"){
		z = 0;
		cout<<"B:init"<<endl;
	}
	void display(){
		cout<<"B:display"<<endl;
	}
	~B(){
		cout<<"B:delete"<<endl;
	}
};

int main(){

	A a(2,2,"a"),*p;
	a.display();

	B b,*q;
	p = &b;
	q = &b;
	p->display();
	b.display();
	q->display();


}


输出:

A:init2
A:display
A:init1
A:init2
B:init
B:display
B:display
B:display
B:delete
A:delete
A:delete
A:delete

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值