C++相较于C最大的一个不同恐怕就在于面向对象的特性,这一特性的发挥可谓贯穿所有常用的C++库,这也是笔者特地花时间学习一下C++的原因。
C++ 类的定义:
类的基本结构如图:
用类定义对象以及访问数据成员:
通过以下的实例,可以演示类的一般使用:
#include <iostream>
using namespace std;
class Box //定义名为Box的类
{
public: //public是成员的访问修饰符,public的成员可以用.(点)来访问
double length; // 长度
double breadth; // 宽度
double height; // 高度
// 成员函数声明,表示的就是这个类里能有啥方法(函数)
//基本形式和一般的函数声明没什么区别
double get(void);
void set( double len, double bre, double hei );
};
// 成员函数定义
//在类之外,对其声明的函数进行定义,此处注意,要用特定的方法指明正在定义类里的函数
double Box::get(void)
{
return length * breadth * height;
}
void Box::set( double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}
int main( )//……省略
{
}
C++类的详解:
一、 C++类成员函数:
C++类成员函数就是指的类所“包含”或者也许可以理解为“具有” 的功能。类成员函数把函数声明为内联函数,不论是否用inline。
言归正传,成员函数可以在类的内部(也就是类的花括号里,在声明函数的同时完成定义),也可以在类的外部,通过范围解析运算符::(也就是上面代码中的定义方式)进行定义。
值得注意的是,不论是在类内还是在类外,成员函数都是可以直接访问类成员的,也就是说不需要“类名.成员”这样访问。
关于内联:
内联指的是,当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。
二、C++ 类访问修饰符:public,private&protected
public: 公有成员在程序中类的外部是可访问的。可以不使用任何成员函数来设置和获取公有变量的值,如下所示:
// 使用成员函数设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
// 不使用成员函数设置长度
line.length = 10.0; // OK: 因为 length 是公有的
cout << "Length of line : " << line.length <<endl;
protected: protected(受保护)成员不可以在类之外被访问,但在派生类(即子类)中是可访问的。
class Box
{
protected:
double width;
};
class SmallBox:Box // SmallBox 是派生类,继承了基类Box的成员width
{
public:
void setSmallWidth( double wid );
double getSmallWidth( void );
};
// 子类的成员函数,可以直接访问基类的成员
double SmallBox::getSmallWidth(void)
{
return width ;
}
void SmallBox::setSmallWidth( double wid )
{
width = wid;
}
private: 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。即便是子类,也无法访问其父类的private成员。只有类和友元函数可以访问私有成员。默认情况下,即不用访问修饰符的情况下,成员默认为私有成员。
实际操作中,我们一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数,如下所示:
#include <iostream>
using namespace std;
class Box
{
public:
double length;
void setWidth( double wid );
double getWidth( void );
private:
double width;
};
// 成员函数定义
double Box::getWidth(void)
{
return width ;
}
void Box::setWidth( double wid )
{
width = wid;
}
// 程序的主函数
int main( )
{
Box box;
// 不使用成员函数设置长度
box.length = 10.0; // OK: 因为 length 是公有的
cout << "Length of box : " << box.length <<endl;
// 不使用成员函数设置宽度
// box.width = 10.0; // Error: 因为 width 是私有的
box.setWidth(10.0); // 使用成员函数设置宽度
cout << "Width of box : " << box.getWidth() <<endl;
return 0;
}
在保护数据的“严格”程度上,依次是public<protected<private,这在三种继承方式中会有所体现。
三、三种继承特点:
C++有public,protected,private三种继承方式,当子类采用更“严格”的方式继承时,父类较低严格程度的成员也会“升级”其访问权限。即:
继承方式 | 基类成员访问属性 | 继承后成员访问属性 |
---|---|---|
public | public,protected,private | public,protected,private |
protected | public,protected,private | protected,protected,private |
private | public,protected,private | private,private,private |
当继承时未指明继承方式,默认按照public继承,即成员访问属性不变。
四、构造函数和析构函数
构造函数和析构函数,是类的特殊成员函数,分别用于创建和删除对象。它们名称与类名相同,不返回任何类型的值(包括void,因此不需要写返回值类型)。
构造函数:
一般情况下,构造函数如下代码所示,可以不带参数
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;//事实上,这一行业可以去掉,函数内容跟为空即可
}
为了初始化方便,构造函数常用于创建对象,并且赋初值:
赋初值有两种方式,一种是普通的赋值:
C::C( double a, double b, double c)
{
X=a;
Y=b;
Z=c;
....
}
另一种则是使用初始化列表进行初始化:
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}
这两种初始化方法等价。
拷贝构造函数:主要作用是使用同一类中之前创建的对象来初始化新创建的对象
classname (const classname &obj) {
// 构造函数的主体
}
四、友元
友元使用friend关键字标记,可以用来标记友元类和友元函数。
友元函数完全不属于类,在类的外部定义,但是却可以访问类的所有成员(包括private和protected成员)。尽管友元函数的原型有需要在类的定义中指明,但是友元函数并不是成员函数。
友元类的整个类和所有成员,都是友元。
友元函数的示例如下:
class Box
{
double width;
public:
double length;
friend void printWidth( Box box );//友元函数
friend class ClassTwoName;
void setWidth( double wid );
};
五、类的指针和this指针
类的指针与指向结构的指针类似,通过成员访问运算符->来访问成员。在类之外,即在使用类时,使用&运算符即可获得类的指针。
this指针是一个特殊的指针,每一个对象都可以通过this指针来访问自己的地址。值得注意的是,this指针是所有成员函数的隐含参数,因此,在成员函数内部,this可以用来指向调用的对象。(所以实际上,在调用一个对象的成员函数时,是用该对象的地址来初始化隐式参数this)在成员函数内,可以显式的使用this获得对象的地址,并利用类指针的方式访问成员以及成员函数。
六、类的静态成员
类的静态成员,包括静态变量和静态函数。它们通过static关键字来标记。
静态可以理解为,类的某种不变量 (比如,可以用来统计某个类有多少个已创建的对象),因此不论某类有多少对象,静态成员都被每个对象所共享。
静态变量:静态变量的初始化比较特别,其初始化不能放在类定义中,但可以在类外,通过解析运算符:: 来访问并初始化,(如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零)。
初始化方式:
// 初始化类 Box 的静态成员
int Box::objectCount = 0;
静态成员函数: 静态成员函数与类有关,与类的任何特定对象无关,因此在对象不存在的情况也可访问,方法是:
ClassName :: static_func_name (类名::静态成员函数名)
需要注意的是以下两点:
- 静态成员函数只能访问静态成员变量、其他静态成员函数和类外部的其他函数
- 静态成员函数没有 this 指针
其实上述特点也较容易理解,其是静态函数与具体的对象无关导致的。
与静态成员变量不同的是,静态成员函数可以在类定义中进行定义。
静态成员(包括变量和函数)的示例如下:
#include <iostream>
using namespace std;
class Box
{
public:
//静态成员数据的声明
static int objectCount;
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// 每次创建对象时增加 1
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
//静态函数的定义
static int getCount()
{
return objectCount;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 初始化类 Box 的静态成员
int Box::objectCount = 0;
int main(void)
{
//静态函数的调用,可见,其是使用类名来调用的
// 在创建对象之前输出对象的总数
cout << "Inital Stage Count: " << Box::getCount() << endl;
Box Box1(3.3, 1.2, 1.5); // 声明 box1
Box Box2(8.5, 6.0, 2.0); // 声明 box2
// 在创建对象之后输出对象的总数
cout << "Final Stage Count: " << Box::getCount() << endl;
return 0;
}
至此,基本完成了C++面向对象中类的基本内容,下一部分快速地过一下继承。
晚安 😃
P.S. 有关内容大多引自菜鸟教程,在其基础上进行了总结,添加了一些自己的理解和注释