本篇博客是对课堂PPT、知识点的整理
目录
前言
面向对象程序设计,一个很重要的点,就是数据的权限,或者说对内部数据的封装。本篇博客主要总结的就是相关对数据权限的操作或者设置,包括:this指针与const特性、友元函数和友元类。此外还对类的复合进行了说明和知识整理。
一、类的复合
- 一个类的对象中,往往包含若干其他类的对象;或者说,一个类的对象往往是由许多其他类的对象组合而成的
- 比如,一个汽车对象中一般包含四个车轮对象
1.实例引入
(1)问题描述
定义一个圆类Circle,利用类的复合定义一个圆柱类Column,然后创建圆柱类Column的实例化对象
(2) 面向对象设计
(3)说明
- 成员对象的构造函数会被自动调用
- 如果成员对象的构造函数需要参数,参数也需要通过复合类的构造函数传递过去
- 成员对象的析构函数会在复合类被撤销时自动调用
(4)实现代码
Circle类
class Circle
{
private:
double x, y, r;
public:
Circle(double px, double py, double pr):
x(px), y(py), r(pr)
{
cout << "调用circle的构造函数" << endl;
}
~Circle()
{
cout << "调用circle的析构函数" << endl;
}
double area()
{
return PI * r * r;
}
};
Column类
class Column
{
private:
Circle circle;
double height;
public:
Column(double px, double py, double pr, double ph):
circle(px, py, pr),
height(ph)
{
cout << "调用column的构造函数" << endl;
}
~Column()
{
cout << "调用column的析构函数" << endl;
}
double Volumn()
{
return circle.area() * height;
}
};
2.值得补充的一点细节 ‘ : ’
值得注意的是,在上述例子,构造函数代码,初始化部分用了冒号 " : "来进行。
在程序中定义变量并初始化的机制中,有两种形式,一个是我们传统的初始化的形式,即
赋值运算符赋值,还有一种是括号赋值。
int a=10;
char b='r';//赋值运算符赋值
int a(10);
char b('r');//括号赋值
但括号赋值只能在变量定义并初始化中,不能用在变量定义后再赋值,这是和赋值运算符赋值的不同之处
int a; //先定义一个变量
......
a=10; //根据需要赋值
int b; //先定义一个变量
......
b(10); //错误:编译器讲b当作函数名,尝试调用
冒号初始化与函数体初始化的区别在于:
冒号初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),那么分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行。对于在函数中初始化,是在所有的数据成员被分配内存空间后才进行的。
因此大致有两种情况必须用" : "进行初始化:
(1) const修饰的数据成员
const修饰常量,即const修饰的对象只能在初始化的时候赋值,因此在分配空间时就进行初始化 。
(2) 类的复合
当子类的构造需要参数时,也需要在分配空间时就进行初始化,因此也需要用" : "进行初始化。
二、this指针与const特性
1.this 指针
- 在每个非静态函数中都能访问一个名为 this 的指针,这个指针恰好指向当前对象
- this 指针的类型与当前类相关:Circle 对象 this 指针的类型为 Circle*, Column 对象 this 指针的类型为 Column*
- 注意, this 指针是常量,不能被重新赋值
(1)this指针的作用
- 定位当前对象:
return * this ;
- 定位当前对象成员:
void * Student::setNumber(int number) { this-> number = number; }
(2)在类的非静态方法中,可使用 this 指针
Stack :: ~Stack() {
this->size = 0;
this->top=0;
objNum--;
delete[] this->data;
}
void Student :: setName(char* name) {
strcpy( this->name, name);
}
(3)实例说明
定义一个类,要求其对象可以连续调用其成员函数,创建该类的对象并连续调用其成员函数
class Test
{
private:
int data;
public:
Test() {
data = 0;
}
~Test(){}
Test& setData(int n) {
data = n;
return *this;
}
void print() {
cout << "data = " << data << endl;
}
};
int main() {
Test t;
t.setData(5).print();
return 0;
}
2.const 特性
- 用 const修饰变量:const int x = 5;
- 用 const 限制函数参数: int sum(const int data[])
- 用 const 修饰指针
- const指针:int* const p = &x; p = &y; (X)
- 指向常量的指针:const int*p = &x; *p = 5; (X)
- 指向常量的const指针:const int* const p = &x;
在面向对象部分,const 关键字有几个额外作用
- 修饰对象
- 修饰方法
- 修饰数据成员
(1) 修饰对象
const 对象
- 当希望某个对象的数据成员不被修改时,可以将其声明为 const 对象,例如: const Circle;
const 对象的使用规则
- const 对象不能调用非 const 方法;
- const 对象成员的赋值只能在构造方法中进行
- 例:
const Complex c (3,5); const Complex * pcs = new Complex(1,2); c. setReal(2); //错误 pst -> setImage(5); //错误
(2)修饰方法
const方法
- 如果希望类的某方法不修改当前对象的数据成员,则可以将该方法声明为 const 方法
- const 关键字应放在函数头部的后面,如
int Complex::getReal() const { return this->real;}
const方法使用限制
- 不能修改当前对象的数据成员的值
(3)修饰数据成员
const 数据成员
- 如果希望某些数据成员在对象产生后不发生修改,则可以将其声明为 const 成员
- 声明 const 数据成员时,应将 const 置于数据成员前。比如:对于 Stack 类,可以将 size 成员作为 const 数据成员,其书写方式为 const int size;
const数据成员的使用限制
- const 数据成员不允许修改
- const 数据成员只能在构造方法中初始化,但不能以赋值方式进行
三、 友元函数和友元类
1.数据封装的“例外”
- 一般情况下,数据成员的访问权限为私有,其值只允许被本类的方法访问;
- 但某些情况下,希望其数据成员能够被个别的外部方法访问。
2.友元函数
- 一个类的友元函数是该类的外部函数,但它有权限访问类的所有成员,包括私有成员和受保护成员;
- 友元函数可以是某个全局函数(不属于任何类的函数),也可以是某个类的成员函数。
class Triangle
{
friend void setA(Triangle &, int);
public:
Triangle(int x=5, int y=5, int z=5):
a(x), b(y), c(z)
{ }
void print() {
cout << a << " " << b << " " << c << endl;
}
private:
int a, b, c;
};
void setA(Triangle & t, int a){
t.a = a;
}
int main() {
Triangle t;
setA(t, 10);
t.print();
return 0;
}
3.友元类
一个类可以把另外一个类声明为其友元类。其友元类的所有成员函数都可以访问该类的全部成员,包括私有成员和受保护成员
(1)友元类声明方法
- 在类定义中,在要声明的友元类的前面加上 friend 关键字
(2)友元类的单向性
- A 为 B 的友元,并不意味着 B 也是 A 的友元
- A为 B 的友元,B 为 C 的友元,并不说明 A是 C 的友元
以数学化(离散数学)的角度来说,友元这种关系不是传递的也不是对称的。
以形式化的方式说,你把真心给那个她,她却未必把真心给你。
(3)实例说明
class B;
class A {
public:
void setB(B&, int);
void print(B &);
};
class B {
friend class A;
private:
int data;
};
void A::print(B& b) {
cout <<“The data of object B:”<<b.data<<endl;
};
总结
本篇内容,对类的复合、this指针与const特性、友元函数和友元类的相关知识进行了汇总。
这些C++的特性,都是围绕一个核心理念:类成员信息的封装以及特殊权限控制。正式因为这些控制方式,才使得在项目实战中,规避了许多难题,也简化了很多实现问题。