C++类与对象

1.怎么去看代类

(1)C++在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。类是对问题的抽象描述,是对对象的相关函数和数据的封装。我们可以从另一个简单的角度理解类,例如,基本数据类型,像int,double等,我们声明一个类型变量。

2.类的意义

(1)类是对对象的抽象,它是由我们根据客观事物抽象而成,形成一类事物;类是一种抽象的数据类型,就像一种模板。

(2)类是把属性和方法进行封装,同时对类的属性和方法进行访问控制。

3.类的特性

类的三大特性是:封装继承多态

(1)封装

封装的定义:就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成,其中数据和函数都是类的成员。

封装的目是:增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。

例如,在抽象的基础上,我们可以将时钟的数据和功能封装起来,构成一个时钟类。

class Clock 
{ 
public : //共有成员,外部借口 
void SetTime ( int NewH , int NewM , int NewS ) ;//通过接口访问类的属性
void ShowTime () ;//通过外部的方法来改变类的属性
private : //私有成员,外部无法访问 
int Hour , Minute , Second ;
}
(2)继承

继承:面向对象程序设计中最重要的一个概念。继承允许我们依据另一个或多个类来定义一个新类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。   依据的已有类称为基类,新建的类称为派生类如果一个类A继承自另一个类B,就把这个A称为“B的子类”,而把B称为“A的父类”。

继承:可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。

例如:

// 基类
class Shape    //物体形状
{
   public:
      void setWidth(int w)
      {
         width = w;
      }
      void setHeight(int h)
      {
         height = h;
      }
   protected:
      int width;
      int height;
};
 
// 派生类
class Rectangle: public Shape     //长方形物体
{
   public:
      int getArea()
      { 
         return (width * height); 
      }
};
(3)多态

多态:按字面的意思就是多种形态。根据所属,又可以把多态分为两种:类间、类内。

(1)类内多态,就是函数的多态。函数名相同,函数的参数个数、参数类型不同,来拓展一个函数的功能。(又叫重载,这是一种编译时的多态)。

(2)类间多态,是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。(这是一种运行时多态)

封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。

多态的作用是:为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。

多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。静态的情况为:基类和子类都有的函数,而基类的相关函数不是虚函数。这就会导致子类在调用相关函数的时候出错。

#include<iostream>  
using namespace std;
class A
{
public:
	void foo()
	{
		cout << 1 << endl;
	}
	virtual void fun()
	{
		cout << 2 << endl;
	}
};
class B : public A
{
public:
	void foo()
	{
		cout << 3 << endl;
	}
	void fun()
	{
		cout << 4 << endl;
	}
};
int main(void)
{
	A a;
	B b;
	A *p = &a;
	p->foo();
	p->fun();
	p = &b;
	p->foo();
	p->fun();
	system("pause");
	return 0;
}
//输出1 2 1 4 因为foo()函数不是多态,基类中的foo()函数不是虚函数。

4.类的组成

根据访问权限,可以把类分为privatepublicprotect三部分组成。根据类的性质,可以把类分为属性行为(类抽象的根据)

(1)private 部分:在类外不可以直接访问。什么叫类外呢,就是实例化的对象或者继承类。如果要类外访问这部分,就要通过共有接口函数(public 部分)实现。

(2)public 部分:可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问,子类的对象访问。

(3)protect 部分:可以被该类中的函数、子类的函数、以及其友元函数访问,不能被该类的对象及子类的对象访问。

例子:

#include<iostream>  
using namespace std;
class A
{
public:
	void foo()
	{
		cout << 1 << endl;
	}
	virtual void fun()
	{
		cout << 2 << endl;
	}
	void test()
	{
		cout << 5 << endl;
	}
private:
	void BasePrivate()
	{
		cout << 6 << endl;
	}
};
class B : public A
{
public:
	void foo()
	{
		cout << 3 << endl;
	}
	void fun()
	{
		cout << 4 << endl;
	}
	void ChildTestBasePrivate()//出错
	{
		BasePrivate();
	}
};
int main(void)
{
	A a;
	B b;
	A *p = &a;
	p->foo();
	p->fun();
	p = &b;
	p->foo();
	p->fun();
	p->test();
	system("pause");
	return 0;
}

(4)当类被继承时

使用 private      继承,父类的所有方法在子类中变为private
使用 protected 继承,父类的 protected public 方法在子类中变为 protected,   private 方法不变; 

使用 public      继承,父类中的方法属性不发生改变;

类组成小结

(1)访问权限:

public:可以被任意实体访问

protected:只允许子类及本类的成员函数访问

private:只允许本类的成员函数访问

(2)继承方式

public 继承

protect 继承

private 继承

(3)继承与访问组合

基类中      继承方式        子类中

public & public继承 => public

public & protected继承 => protected

public & private继承 = > private


protected & public继承 => protected

protected & protected继承 => protected

protected & private继承 = > private


private & public继承 => 子类无权访问

private & protected继承 => 子类无权访问

private & private继承 = > 子类无权访问

基类中 继承方式 子类中

public & public继承 => public

public & protected继承 => protected

public & private继承 = > private


protected & public继承 => protected

protected & protected继承 => protected

protected & private继承 = > private


private & public继承 => 子类无权访问

private & protected继承 => 子类无权访问

private & private继承 = > 子类无权访问


小结参考: https://blog.csdn.net/scottly1/article/details/24354489


类之构造函数

(1)构造函数是一种可初始化其类的实例的成员函数。 构造函数具有与类相同的名称,没有返回值。 构造函数可以具有任意数量的参数,类可以具有任意数量的重载构造函数。 构造函数可以具有任何可访问性(公共、受保护或私有)。 如果未定义任何构造函数,则编译器会生成不采用任何参数的默认构造函数;可以通过将默认构造函数声明为已删除来重写此行为。

(2)构造函数顺序:(1)声明顺序调用基类成员构造函数.(2)如果类派生自虚拟基类,则会将对象的虚拟基指针初始化。(3)如果类具有继承了虚函数,则会将对象的虚函数指针初始化虚函数指针指向类中的虚函数表,确保虚函数正确地调用绑定代码。(4)它执行自己函数体中的所有代码.

例如:

#include <iostream>  
using namespace std;

class Contained1 
{
public:
	Contained1()
	{
		cout << "Contained1 constructor." << endl;
	}
};

class Contained2
{
public:
	Contained2()
	{
		cout << "Contained2 constructor." << endl;
	}
};

class Contained3
{
public:
	Contained3()
	{
		cout << "Contained3 constructor." << endl;
	}
};

class BaseContainer 
{
public:
	BaseContainer() 
	{
		cout << "BaseContainer constructor." << endl;
	}
private:
	Contained1 c1;
	Contained2 c2;
};

class DerivedContainer : public BaseContainer
{
public:
	DerivedContainer() : BaseContainer() 
	{
		cout << "DerivedContainer constructor." << endl;
	}
private:
	Contained3 c3;
};

int main() 
{
	DerivedContainer dc;
	return 0;
}
//cout
//Contained1 constructor.
//Contained2 constructor.
//BaseContainer constructor.
//Contained3 constructor.
//DerivedContainer constructor.

如果构造函数引发异常,析构的顺序与构造的顺序相反

1.  构造函数主体中的代码将展开。

2.  基类和成员对象将被销毁,顺序与声明顺序相反。

3.  如果是非委托构造函数,所有完全构造的基类对象和成员均将被销毁。但是,对象本身不是完全构造的,因此析构函数不会运行。

注意:使用成员初始值设定项列表从构造函数参数初始化类成员。此方法使用直接初始化,这比在构造函数体内使用赋值运算符更高效

class Box 
{
public:
	Box(int width, int length, int height)
		: m_width(width), m_length(length), m_height(height) // member init list  跟高效的初始化方式
	{}
	int Volume() { return m_width * m_length * m_height; }
private:
	int m_width;
	int m_length;
	int m_height;

};



显示构造函数

如果类具有带一个参数的构造函数,或是如果除了一个参数之外的所有参数都具有默认值,则参数类型可以隐式转换为类类型。例如,如果 Box 类具有一个类似于下面这样的构造函数:

Box(int size) : m_width(size), m_length(size), m_height(size) {}

可以初始化 Box,如下所示

Box b = 99;

或将一个 int 传递给采用 Box 的函数:

class Box 
{
public:
	Box(int width, int length, int height)
		: m_width(width), m_length(length), m_height(height) // member init list  
	{}
	Box(int size) : m_width(size), m_length(size), m_height(size) {}
	int Volume() { return m_width * m_length * m_height; }
private:
	int m_width;
	int m_length;
	int m_height;

};

class ShippingOrder
{
public:
	ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage) {}

private:
	Box m_box;
	double m_postage;
};

int main()
{
	ShippingOrder so(42, 10.8);
	return 0;
}

这类转换可能在某些情况下很有用,但更常见的是,它们可能会导致代码中发生细微但严重的错误。作为一般规则,应对构造函数使用 explicit 关键字(和用户定义的运算符)以防止出现这种隐式类型转换:


#include <iostream>  
using namespace std;

class Box 
{
public:
	Box(int width, int length, int height)
		: m_width(width), m_length(length), m_height(height) // member init list  
	{}
	explicit  Box(int size) : m_width(size), m_length(size), m_height(size) {}
	int Volume() { return m_width * m_length * m_height; }
private:
	int m_width;
	int m_length;
	int m_height;

};

class ShippingOrder
{
public:
	ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage) {}

private:
	Box m_box;
	double m_postage;
};

int main()
{
	ShippingOrder so(42, 10.8);
	return 0;
}

构造函数是显式函数时,此行会导致编译器错误。正确的方式如下:

#include <iostream>  
using namespace std;

class Box 
{
public:
	Box(int width, int length, int height)
		: m_width(width), m_length(length), m_height(height) // member init list  
	{}
	explicit  Box(int size) : m_width(size), m_length(size), m_height(size) {}
	int Volume() { return m_width * m_length * m_height; }
private:
	int m_width;
	int m_length;
	int m_height;

};

class ShippingOrder
{
public:
	ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage) {}

private:
	Box m_box;
	double m_postage;
};

int main()
{
	ShippingOrder so(Box(42), 10.8);
	return 0;
}



默认构造函数

默认构造函数没有参数;它们遵循略有不同的规则:

默认构造函数是一个特殊成员函数如果没有在类中声明构造函数,则编译器会提供默认构造函数

#include <iostream>  
using namespace std;

class Box 
{
public:
	Box(int width, int length, int height)
		: m_width(width), m_length(length), m_height(height) // member init list  
	{}
//	Box(int size) : m_width(size), m_length(size), m_height(size) {}
	int Volume() { return m_width * m_length * m_height; }
private:
	int m_width;
	int m_length;
	int m_height;

};

class ShippingOrder
{
public:
//	ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage) {}

private:
//	Box m_box;
	double m_postage;
};

int main()
{
	//	ShippingOrder so(Box(42), 10.8);

	Box box1{}; // call compiler-generated default ctor  
	Box box2;   // call compiler-generated default ctor  
	return 0;
}

系统报错,因为已经存在构造函数,系统不会在提供默认构造函数。


当你调用默认构造函数并尝试使用括号时,系统将发出警告:

class myclass {};
int main()
{
	//	ShippingOrder so(Box(42), 10.8);
	myclass mc();     // warning C4930: prototyped function not called (was a variable definition intended?)  

	return 0;
}



复制构造函数与移动构造函数

复制构造函数是特殊成员函数,它采用对相同类型对象的引用作为输入,并创建它的副本。“赋值”意味着复制赋值,赋值操作和初始化操作都会导致对象被复制。

赋值在将一个对象的值赋给另一个对象时,第一个对象将复制到第二个对象中。例如:

Point a, b;
...
a = b;

导致 b 的值被复制到 a 中。

初始化:在以下情况下将进行初始化:声明新对象、参数通过值传递给函数或值通过值从函数返回。

复制构造函数采用了 class-name& 类型的参数,其中 class-name 是为其定义构造函数的类的名称:

// spec1_copying_class_objects.cpp  
class Window  
{  
public:  
    Window( const Window& ); // Declare copy constructor.  
    // ...  
};
参考: https://msdn.microsoft.com/zh-cn/library/s16xw1a8.aspx



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值