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.类的组成
根据访问权限,可以把类分为private、public、protect三部分组成。根据类的性质,可以把类分为属性、行为(类抽象的根据)。
(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继承 = > 子类无权访问
类之构造函数
(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