Trong lập trình hướng đối tượng có 3 thuộc tính chính là public, private và protected. Người lập trình sử dụng các thuộc tính này để đảm bảo tính đóng gói và che giấu thông tin phần mềm. Tuy nhiên ngôn ngữ lập trình C++ còn cho phép người lập trình cấp quyền cho một hàm, một lớp truy cập dữ liệu của một lớp khác và sử dụng chúng một cách toàn quyền.
Xem thêm: Sử dụng hàm bạn (friend) cho 2 Class
1. Đặt vấn đề cần truy cập dữ liệu
Giả sử chúng ta có 2 class là class Matrix và class Vector. Sau đó chúng ta muốn viết hàm nhân Ma trận với vector.
Đối với hàm nhân:
- Chúng ta không thể viết hàm nhân thuộc class Vector
- Không thể thuộc lớp Matrix
- Không thể viết một hàm tự do (vì hàm không thuộc class sẽ không truy cập các thuộc tính private được)
Lúc này ngôn ngữ lập trình hỗ trợ cho chúng ta xây dựng hàm tự do để truy cập được các dữ liệu của 2 class trên, đó chính là hàm bạn friend.
2. Hàm bạn (friend) trong c++
a. Giới thiệu về hàm bạn trong c++
– Hàm bạn trong c++ là hàm tự do, không thuộc lớp. Tuy nhiên hàm bạn trong c++ có quyền truy cập các thành viên private của class.
– Một lớp trong c++ có thể có nhiều hàm bạn, và chúng phải nằm bên ngoài class.
b. ưu điểm của hàm bạn trong c++
– Kiểm soát các truy nhập ở cấp độ lớp. Nghĩa là không thể áp đặt hàm bạn cho một class, nếu như chưa khai báo hàm bạn trong class.
– Giải quyết được vấn đề cần truy cập dữ liệu của class như vấn đề minh họa bên trên.
c. Cú pháp khai báo hàm bạn trong c++
Đặt từ khóa friend trước khai báo hàm thông thường trong class.
Ví dụ như trong vấn đề bên trên chúng ta sẽ khai báo như sau:
Matrix.h
#pragma once class Vector; class Matrix { private: int m; // dòng int n; // cột double **elements; public: Matrix(); ~Matrix(); Matrix(const Matrix & a); void nhap(); void xuat(); int Cong(const Matrix & a); // return 1 nếu cộng dc void Nhan(const double & k);// Nhân với 1 số K int Nhan(const Matrix & a);// NHân với 1 CMatrix, return 1 nếu nhân đc friend Vector multiply(const Matrix &a, const Vector &b); };
Vector.h
#pragma once class Matrix; class Vector { private: double *coords; int n; public: Vector(); Vector(int N, double x); // tạo vector có N chiều, mỗi ô có giá trị x Vector(const Vector &a); ~Vector(); void nhap(); void xuat(); int Cong(const Vector &a); // return 1 nếu cộng đc void NhanK(const double &k); int Tru(Vector a); // return 1 nếu trừ đc double TichVoHuong(const Vector &a); friend Vector multiply(const Matrix &a, const Vector &b); };
Main.cpp
Và nội dung của hàm bạn được đặt tự do bên ngoài, không thuộc phạm vi class
#include <iostream> using namespace std; #include "Vector.h" #include "Matrix.h" Vector multiply(const Matrix &a, const Vector &b) { if (a.n != b.n) { cout << "Ko nhan duoc!\n"; return Vector(0,0); } Vector res(a.m, 0); res.n = a.m; for (int i = 0; i < a.m; i++) { res.coords[i] = 0; for (int j = 0; j < b.n; j++) res.coords[i] = res.coords[i] + b.coords[j] * a.elements[i][j]; } return res; } int main() { //.................... system("pause"); return 0; }
Trong code mẫu trên bạn phải khai báo friend Vector multiply(const Matrix &a, const Vector &b); Trong cả 2 class Vector và Matrix là vì hàm này cần truy cập vào 2 class trên. Nên bạn phải thông báo cho chương trình biết, đây là hàm bạn của class để nó được quyền truy cập class như đang ở bên trong class.
3. Lớp bạn (class friend) trong c++
Lớp bạn trong c++ cũng tương tự như hàm bạn, việc khai báo lớp bạn sẽ cho phép lớp bạn của lớp kia được truy cập tất cả thành viên của nó.
a. Tính chất mối quan hệ
- Khai báo A là bạn của lớp B không có nghĩa B xem A là bạn, điều đó có nghĩa là chỉ có lớp A truy cập được thành viên của lớp B, Nhưng lớp B không thể truy cập ngược lại của lớp A.
- Không đối xứng.
- Không bắc cầu.
b. Cú pháp khai báo lớp bạn
Bạn cũng dùng từ khóa friend để khai báo, ví dụ:
class TOM { public: friend class JERRY; //Có lớp bạn là JERRY private: int SecretTom; //Bí mật của TOM }; class JERRY { public: void Change(TOM T) { T.SecterTom++; //Bạn nên có thể thay thế } };
Trong ví dụ trên mình khai báo lớp Jerry là bạn của lớp Tom bằng friend class JERRY
Và lúc này Lớp JERRY có thể truy cập được tất cả các thành phần của lớp TOM ví dụ như T.SecterTom++;
4. Bài tập ứng dụng hàm bạn và lớp bạn trong c++
a. Bài tập OOP nhân Ma trận với Vector C++
– Định nghĩa lớp Vector trong không gian có số chiều bất kì. Xây dựng các hàm thực hiện tính cộng trừ nhân (nhân vector với một hệ số k, tích vô hướng của 2 vector) và các phương thức cần thiết.
– Định nghĩa lớp Matrix trong không gian có kích thước bất kì. Xây dựng các hàm thực hiện tính cộng, nhân ma trận với một số k, nhân ma trận với ma trận
– Sử dụng hàm bạn để xây dựng hàm nhân ma trận với vector.
b. Code Bài tập OOP nhân Ma trận với Vector C++
b.1 Lớp vector
Vector.h
#pragma once class Matrix; class Vector { private: double *coords; int n; public: Vector(); Vector(int N, double x); // tạo vector có N chiều, mỗi ô có giá trị x Vector(const Vector &a); ~Vector(); void nhap(); void xuat(); int Cong(const Vector &a); // return 1 nếu cộng đc void NhanK(const double &k); int Tru(Vector a); // return 1 nếu trừ đc double TichVoHuong(const Vector &a); friend Vector multiply(const Matrix &a, const Vector &b); };
Vector.cpp
#pragma once #include "Vector.h" #include "Matrix.h" #include <iostream> using namespace std; Vector::Vector() { coords = NULL; } Vector::Vector(int N, double x) { n = N; coords = new double[n]; for (int i = 0; i < n; i++) coords[i] = x; } Vector::Vector(const Vector & a) { n = a.n; coords = new double[n]; for (int i = 0; i < n; i++) coords[i] = a.coords[i]; } Vector::~Vector() { if (coords != NULL) delete[] coords; } void Vector::nhap() { cin >> n; coords = new double[n]; for (int i = 0; i < n; i++) cin >> coords[i]; } void Vector::xuat() { for (int i = 0; i < n; i++) cout << roundf(coords[i] * 100) / 100 << " "; cout << endl; } int Vector::Cong(const Vector & a) { if (n != a.n) return 0; for (int i = 0; i < n; i++) coords[i] += a.coords[i]; return 1; } void Vector::NhanK(const double & k) { for (int i = 0; i < n; i++) coords[i] *= k; } int Vector::Tru(Vector a) { if (n != a.n) return 0; a.NhanK(-1); Cong(a); return 1; } double Vector::TichVoHuong(const Vector &a) { double res = 0; for (int i = 0; i < n; i++) res += coords[i] * a.coords[i]; return res; }
b.2 Lớp matrix
Matrix.h
#pragma once class Vector; class Matrix { private: int m; // dòng int n; // cột double **elements; public: Matrix(); ~Matrix(); Matrix(const Matrix & a); void nhap(); void xuat(); int Cong(const Matrix & a); // return 1 nếu cộng dc void Nhan(const double & k);// Nhân với 1 số K int Nhan(const Matrix & a);// NHân với 1 CMatrix, return 1 nếu nhân đc friend Vector multiply(const Matrix &a, const Vector &b); };
Matrix.cpp
#pragma once #include "Vector.h" #include "Matrix.h" #include <iostream> using namespace std; Matrix::Matrix() { m = 0; n = 0; elements = NULL; } Matrix::~Matrix() { if (elements == NULL) return; for (int i = 0; i < m; i++) { delete[] elements[i]; elements[i] = NULL; } delete[] elements; elements = NULL; } Matrix::Matrix(const Matrix & a) { m = a.m; n = a.n; elements = new double *[m]; // cấp phát m dòng for (int i = 0; i<m; i++) elements[i] = new double[n]; // cấp phát n cột for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) elements[i][j] = a.elements[i][j]; } void Matrix::nhap() { cin >> m >> n; elements = new double * [m]; // cấp phát m dòng for (int i=0; i<m; i++) elements[i] = new double [n]; // cấp phát n cột for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) cin >> elements[i][j]; } void Matrix::xuat() { cout << "Matrix: \n"; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) cout << elements[i][j] << " "; cout << endl; } } int Matrix::Cong(const Matrix & a) { if (!(n == a.n && m == a.m)) return 0; for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) elements[i][j] += a.elements[i][j]; return 1; } void Matrix::Nhan(const double & k) { for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) elements[i][j] *= k; } int Matrix::Nhan(const Matrix & a) { if (n != a.m) return 0; double ** elementsNew = new double *[m]; // tạo vùng nhớ chứa ma trận mới for (int i = 0; i < m; i++) elementsNew[i] = new double[a.n]; for (int i = 0; i < m; i++) // nhân { for (int j = 0; j < a.n; j++) { elementsNew[i][j] = 0; for (int k = 0; k < n; k++) elementsNew[i][j] = elementsNew[i][j] + elements[i][k] * a.elements[k][j]; } } // xóa vùng nhớ cũ for (int i = 0; i < m; i++) delete[] elements[i]; delete[] elements; // cập nhật lại kích thước ma trận m = m; n = a.n; elements = elementsNew; // gán lại vùng nhớ mới. return 1; }
b.3 Khai báo hàm bạn
main.cpp
#include <iostream> using namespace std; #include "Vector.h" #include "Matrix.h" Vector multiply(const Matrix &a, const Vector &b) { if (a.n != b.n) { cout << "Ko nhan duoc!\n"; return Vector(0,0); } Vector res(a.m, 0); res.n = a.m; for (int i = 0; i < a.m; i++) { res.coords[i] = 0; for (int j = 0; j < b.n; j++) res.coords[i] = res.coords[i] + b.coords[j] * a.elements[i][j]; } return res; } int main() { Matrix C; C.nhap(); Vector D; D.nhap(); Vector RES = multiply(C, D); RES.xuat(); system("pause"); return 0; }