C++在C语言基础上增加了面向对象的编程,支持面向对象的程序设计。
类是C++的核心特性,通常被称为用户定义的类型。
类用于指定对象的形式,包含(类的成员)
定义一个类 = 定义一个数据类型的蓝图。定义过程中并未定义任何数据,但是定义了类的对象包括了什么,可以做什么操作。
class classname
//class 为关键字
{
access specifiers:
//访问修饰符:private[default]/public/protected
members/variables;
//变量
member functions(){
}
//方法
};
//需要用到分号来结束一个类
class Box
{
public:
double length;
double width;
double height;
double getVol(void){
}
};
double Box::getVol(void) //作用域区分符(::)被用到
{
return length * width * height;
}
在类的对象的作用域内,公共成员(public)在类的外部是可访问的。
类提供了对象的蓝图。
Box box1;
Box box2;
类的对象的公共数据成员可以使用直接成员访问运算符(.)来访问
box1.length = 3.0;
box1.width = 3.0;
box1.height = 3.0;
double vol;
vol = box1.getVol();
定义类成员函数时,行数将被声明为内联的,即便没有使用inline标识符。将类成员函数声明在类的定义内,而将该函数的定义单独写在类的定义外是一种良好的编程风格。次数需要用到作用域区分符或范围解析运算符(::)。在该运算符前,应放置该类成员函数所在类的名称(而访问对的对象的公共成员时,在直接成员访问运算符前应放置类的对象的名称)。
(::)可以不跟类名,表示全局数据或者全局函数(即非成员函数)。
int a;
int b;
void foo(int m, int n)
{
::a = m;
::b = n;
}
class Foo
{
public:
void foo(int m, int n) //成员函数
{
::foo(m, n); //非成员函数
}
private:
int a;
int b;
};
# 类访问修饰符
**数据封装**是**面向对象编程**的一个重要特点,它放置函数直接访问类类型的内部成员。类成员的访问限制通过类主题内部对各个区域标记**访问修饰符**来指定(private(默认)/public/protected)。
## 共有(public)成员
可在类的外部直接访问,允许直接获取和设置共有变量的值。
## 私有(private)成员
不可在类外部访问,甚至不可以查看。只有**类**和**友元函数**可以访问私有成员。
实际操作中,我们一般会在私有区域定义数据,在共有区域定义相关函数,以便在类的外部也可以调用这些函数。
## 受保护(protected)成员
受保护成员与私有成员的一点不同是:**受保护成员在派生类(子类)中是可以访问的**。
```cpp
class Box
{
protected:
double width;
};
//Box为父类
class SmallBox:Box
//SmallBox为派生类,默认private继承方式
{
public
void setSmallWidth( double wid );
double getSmallWidth( void );
};
//以下为子类成员函数的定义
double SmallBox::getSmallWidth( void )
{
return width;
}
void SmallBox::setSmallWidth( double wid )
{
width = wid;
}
有 public,protected,private 三种继承方式,它们相应地改变了基类成员的访问属性。
继承方式 | 基类private的派生类 | 基类protected的派生类 | 基类private的派生类 |
---|---|---|---|
public继承 | public | protected | private |
protected继承 | protected | protected | private |
private继承 | private | private | private |
继承(若未声明继承方式,则默认为private继承,而在struct中默认为public继承)
class A{
.
.
.
};
class B : public A{
.
.
.
};
类的构造函数是类的一种特殊成员函数,它会在每次创建类的新对象时执行。其名称与类的名称完全相同,且不会返回任何类型,包括void。
class Object
{
public:
void dothis( double num );
int dothat( void );
Object(); //构造函数
private:
double number;
};
//成员函数定义,包括构造函数
Object::Object( void );
{
cout << "My man! " << endl;
}
void Object::dothis( double num )
{
number = num;
}
double Object::dothat( void )
{
return number;
}
构造函数可用于为某些成员变量设置初始值。
Object::Object (double num )
{
cout << "My man! You have " << num << endl;
number = num;
}
//在定义类的对象时
Object thingy(100.0);
Object:Object( double num ): number(num)
{
cout << "My man! You have " << num << endl;
}
//次语法等同于上一例
若有多个字段需要初始化,需要用逗号进行分隔。若出现某成员变量初始化需要依赖其他成员的值时,需按照声明变量的顺序进行排列,否则会出现失效。
foo::foo( int a, int b): A(a), B(b)
{
...
}
析构函数的作用是删除该类所创建的对象,有助于跳出程序。它不返回任何值,也不带有任何参数。
析构函数的名称与类的名称完全相同,只是前面加了个波浪号(~)作为前缀。
class Object
{
~Object();
};
Object::~Object( void )
{
cout << "My man! You got nothing left! " << endl;
}
拷贝构造函数时一种特殊的构造函数,使用同一类中先前创建的对象来初始化新的对象,常用于
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造参数。
【Runoob中用户 归尘 的解释:浅拷贝-默认拷贝构造函数的拷贝方式,不为新对象另外分配内存资源,当数据成员有指针时,两个对象的指针会指向同一块内存空间,在析构时,会导致指针悬挂现象;深拷贝-当数据成员中有指针类型时,需构造特定拷贝构造参数,以在实现原有、新增对象之间数据成员的拷贝的同时,为新的对象单独分配内存资源。 防止默认拷贝发生的方法是,声明一个私有的拷贝构造函数,从而避免按值传递或返回对象。】(而下面贴的Runoob例程中则专门在构造函数和拷贝构造函数中为指针变量申请了新的内存空间,避免了上述现象。)
最常见形式:
classname ( const classname &obj ){
//构造函数主体
}
obj是一个 对象引用 ,用于初始化另一个对象。
Runoob例程理解:
例程1:
#include
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int *ptr;
};
// 成员函数定义,包括构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函数
int main( )
{
Line line(10);
display(line);
return 0;
}
结果1:
调用构造函数 //这是Line line(10);创建对象时对构造函数的调用
调用拷贝构造函数并为指针 ptr 分配内存 //这是line被传值传参时,被拷贝一份时,对拷贝构造函数的调用
line 大小 : 10
释放内存 //这是函数返回后,删除临时拷贝的对象时,对析构函数的调用
释放内存 //这是主程序返回时,删除对象时,对析构函数的调用
例程2:
#include
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int *ptr;
};
// 成员函数定义,包括构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函数
int main( )
{
Line line1(10);
Line line2 = line1; // 这里也调用了拷贝构造函数
display(line1);
display(line2);
return 0;
}
结果2:
调用构造函数 //这是Line line(10);创建对象时对构造函数的调用
调用拷贝构造函数并为指针 ptr 分配内存 //这是Line line2 - line1;创建对象时通过调用拷贝构造函数对新对象进行初始化
调用拷贝构造函数并为指针 ptr 分配内存 //这是传值传参创建对象临时拷贝时对拷贝构造函数的调用
line 大小 : 10
释放内存 //这是函数结束后删除临时拷贝对象时对析构函数的调用
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
释放内存 //后两个对析构函数的调用来自主函数结束时删除对象的操作
释放内存
而利用对象的引用为函数传参时,不会调用拷贝构造函数。