1. Định nghĩa lớp:
2. Tạo đối tượng của một lớp và cách truy cập đến phương thức và thuộc tính của lớp
3. Các từ khóa phạm về vi truy cập các trường trong một lớp
4. Hàm copy contructor mặc định và hàm copy contructor:
Mục đích: copy contructor là hàm tạo đặc biệt của C++, được sử dụng với mục đích tạo ra một bản sao của một đối tượng đã có, được truyền vào thông qua tham chiếu hằng của địa chỉ đến đối tượng đó.
Hàm sao chép mặc định (copy contructor default)
- Về hàm sao chép mặc định và cách hoạt động của chúng
Hàm sao chép (copy contructor)
- Khi có ít nhất một trường trong đối tượng là kiểu con trỏ (hoặc tham chiếu) thì ta phải viết lại hàm sao chép.
- Các dạng của hàm sao chép như sau
Ví dụ về hàm sao chép: copy constructor
Nếu các trường không là con trỏ: qua ví dụ dưới đây vùng nhớ giữa các các biến progame.age và manager.age là khác nhau.
Nếu thay 1 số trường là con trỏ:
-->Hướng giải quyết trường hợp này: không sử dụng hàm sao chép mặc định bằng cách viết thêm hàm copy constructor xem code bên dưới
Hỏi: 2 hàm sau
6.Con trỏ this
7. Kế thừa
8. Đa hình (Polymorphism)
Quy tắc về cách gọi các phương thức ảo:
Ví dụ: Tạo lớp Cat kế thừa từ lớp Animal
Trong lớp Animal có hàm:
public:
virtual void Eat() {std::cout << "Animal: Eat\n";};
void Run() {std::cout << "Animal: Run\n";};
Trong lớp Cat overiding lại 2 hàm ở lớp cha Animal:
virtual void Eat() {std::cout << "Cat: Eat\n";};
void Run() {std::cout << "Cat: Run\n";};
Trong hàm main.cpp:
Animal *obj;//obj là con trỏ có KIỂU Animal và trỏ tới ĐỐI TƯỢNG Cat
obj = new Cat();
obj->Eat();//--> Cat:Eat
obj->Run();//--> Aniaml:Run
Kết quả:
Ví dụ:
Tạo lớp A có hàm virtual hienthi() và hàm Todo()
lớp B kế thừa từ lớp A: cài đặt lại hàm hiển thị() và hàm Todo()
lớp C kế thừa từ lớp B: cài đặt lại hàm hiển thị() và hàm Todo()
lớp D kế thừa từ lớp A: cài đặt lại hàm hiển thị() và hàm Todo()
9. Overriding vs. Overloading
10. Operator overloading
11.Class’ static member
2. Tạo đối tượng của một lớp và cách truy cập đến phương thức và thuộc tính của lớp
3. Các từ khóa phạm về vi truy cập các trường trong một lớp
4. Hàm copy contructor mặc định và hàm copy contructor:
Mục đích: copy contructor là hàm tạo đặc biệt của C++, được sử dụng với mục đích tạo ra một bản sao của một đối tượng đã có, được truyền vào thông qua tham chiếu hằng của địa chỉ đến đối tượng đó.
Hàm sao chép mặc định (copy contructor default)
- Về hàm sao chép mặc định và cách hoạt động của chúng
Hàm sao chép (copy contructor)
- Khi có ít nhất một trường trong đối tượng là kiểu con trỏ (hoặc tham chiếu) thì ta phải viết lại hàm sao chép.
- Các dạng của hàm sao chép như sau
X (const X& copy_from_me)
X (X* copy_from_me)
X (X& copy_from_me)
X (const
X©_from_me, int = 10, float = 1.0)
Ví dụ về hàm sao chép: copy constructor
Nếu các trường không là con trỏ: qua ví dụ dưới đây vùng nhớ giữa các các biến progame.age và manager.age là khác nhau.
#include <iostream>
using namespace
std;
class Employee{
public:
int age;
Employee(int x) { //Hàm constructor có tham số
age = x;
}
int GetAge() {
return age;
}
void SetAge(int x){
age = x;
}
};
void main()
{
Employee
programmer(25);
cout <<
programmer.GetAge() << endl;
Employee
manager = programmer); //sẽ gọi hàm sao chép mặc định
cout <<
manager.GetAge() << endl;
manager.SetAge(30);//Vùng nhớ khác nhau nên khi thay đổi nội dung biến manager.age thì giá trị ở progammer.age vẫn không thay đổi như ví dụ dưới đây
getchar();
}
Nếu thay 1 số trường là con trỏ:
#include <iostream>
using namespace
std;
class Employee{
public:
int *age;
Employee(int x){
age = new int;
*age = x;
}
int GetAge(){
return *age;
}
void SetAge(int x){
*age = x;
}
~Employee(){
delete age;
age = NULL;
}
};
void main()
{
Employee
programmer(25);
cout << "programmer: " <<
programmer.GetAge() << endl;
Employee
manager = programmer;//Sẽ gọi hàm SAO CHÉP MẶC ĐỊNH nên
vùng nhớ //của programmer.age và manager.age cùng trỏ vào một vùng
cout << "manager:
" << manager.GetAge() << endl;
manager.SetAge(30);//Thay đổi vùng nhớ age của manager thì vùng nhớ programmer.age thay đổi theo
//Hướng
giải quyết --> Viết lại hàm copy contructor thay hàm copy constructor
default
cout << "programmer: " <<
programmer.GetAge() << endl;//kết quả
thay đổi
cout << "manager:
" << manager.GetAge() << endl;
getchar();
}
-->Hướng giải quyết trường hợp này: không sử dụng hàm sao chép mặc định bằng cách viết thêm hàm copy constructor xem code bên dưới
#include <iostream>
using namespace
std;
class Employee{
public:
int *age;
Employee(int x){
age = new int;
*age = x;
}
Employee(Employee
&people){//Viết thêm hàm copy constructor này
age = new int;
*age =
*people.age;
}
int GetAge(){
return *age;
}
void SetAge(int x){
*age = x;
}
~Employee(){
delete
age;
age = NULL;
}
};
void main()
{
Employee
programmer(25);
cout << "programmer: " <<
programmer.GetAge() << endl;
Employee
manager = programmer;//sẽ gọi hàm copy constructor viết
thêm ở trên
//programmer.age và manager.age sẽ trỏ vào 2 vùng nhớ khác
nhau
cout << "manager:
" << manager.GetAge() << endl;
manager.SetAge(30);
cout << "programmer: " <<
programmer.GetAge() << endl;
cout << "manager: " << manager.GetAge()
<< endl; //chỉ
thay đổi manager.age
getchar();
}
Hỏi: 2 hàm sau
Employee(Employee &people, int x){//Hàm này là hàm constructor không ?
//*age = x;//*people.age;
}
5. Hàm hủy
Employee(Employee &people, int x = 10){//Viết thêm hàm copy constructor này
age = new int;
*age = x;//*people.age;
}
Ví dụ về hàm sao chép:
Ví dụ về hàm :
Ví dụ về hàm sao chép:
#include <iostream>
using namespace std;
class Employee{
private:
int _id;
char *_name;
public:
Employee(char *name, int id);
//Employee(Employee people);//copy constructor default hàm sao chép mặc định
//Employee(Employee &human);//copy constructor hàm sao chép
~Employee();
char *GetName(){
return _name;
}
int GetId(){
return _id;
}
};
Employee::Employee(char *name, int id)
{
_id = id;
_name = new char[strlen(name) + 1];
strcpy(_name, name);
}
//Employee::Employee(Employee &humman)//copy constructor
//{
// _id = humman.GetId();
// _name = new char[strlen(humman._name) + 1];
// strcpy(_name, humman._name);
//}
Employee::~Employee()
{
delete []_name;
}
void main()
{
Employee programmer("ABC", 31);//Khởi tạo
cout << programmer.GetName() << endl;
Employee manager(programmer);//Nếu sử dụng copy contructor mặc định địa chỉ ô nhớ của progame._name và manager._name giống nhau
//dẫn đến nếu thay đổi nội dung của một người thì người kia cũng đổi theo
cout << manager.GetName() << endl;
//strcpy(manager._name, "HIE");//Nếu set lại name thì người kia cũng đổi theo
getchar();
}
Ví dụ về hàm :
#include <iostream>
using namespace
std;
class Student{
private:
char *_name;
public:
Student(){
_name =
NULL;
}
Student(const Student &student){
_name = new char[strlen(student._name)
+ 1];
strcpy(_name,
student._name);
}
void SetName(const char *str){
//Việc kiểm tra biến _name ở (1) để xem biến _name có trỏ tớ
vùng nhớ nào không ?
//Nếu có trỏ tới vùng nhớ nào đó thì ta phải xóa vùng nhớ đó
trước khi cấp phát
//vùng nhớ mới (2) cho biến _name để tránh trường hợp vùng
nhớ cũ không được quản lý
//bởi con trỏ nào --> dẫn đến lead memory.
if(_name != NULL) {//(1)
delete []_name;//Nếu không
xóa vùng nhớ cũ trước khi cấp phát vùng nhớ mới thì sẽ bị lead memory
}
_name = new char[strlen(str)
+ 1];//(2)
strcpy(_name,
str);
}
void Print(){
cout
<< _name << endl;
}
~Student(){
if (_name != NULL)
{
delete []_name;
}
}
};
void main()
{
Student a;
a.SetName("Nguyen Van A");
a.Print();
Student b = a;
b.SetName("Nguyen Van B");
b.Print();
getchar();
}
6.Con trỏ this
7. Kế thừa
8. Đa hình (Polymorphism)
1. Tính trừu tượng trong C++,
cài đặt với từ khóa virtual. Từ khóa virtual không được đặt ra ngoài lớp
2. Không thể tạo ra một đối tượng từ lớp trừu tượng. Lớp trừu tượng là lớp có chứa các phương thức ảo thuần túy như sau:
Virutual void
tên_phương_thức() = 0;//gán bằng 0 thay cho việc cài đặt phương thức này
3. Bất kỳ lớp dẫn xuất từ một lớp cơ sở trừu tượng phải định nghĩa lại tất cả các phương thức thuần túy ảo mà nó thừa hưởng:
- Bằng các phương thức ảo thuần túy
- Hoặc những định nghĩa thực sự
Quy tắc về cách gọi các phương thức ảo:
Lời gọi phương thức tĩnh
|
Lời gọi phương thức ảo
|
1. Nếu lời gọi xuất phát từ một đối tượng của lớp nào thì
phương thức của lớp đó sẽ được gọi
|
|
2. Nếu lời gọi xuất phát từ một con trỏ kiểu lớp nào, thì phương thức của lớp đó sẽ được gọi bất
kể con trỏ chứa địa chỉ của đối tượng nào.
|
2. Nếu lời gọi tới phương thức ảo từ một con trỏ thì phụ
thuộc vào đối tượng cụ thể mà con trỏ đó trỏ tới: con trỏ đang trỏ tới đối tượng của lớp nào
thì phương thức của lớp
đó sẽ được gọi.
|
virtual void Eat() {std::cout << "Animal: Eat\n";};
void Run() {std::cout << "Animal: Run\n";};
Trong lớp Cat overiding lại 2 hàm ở lớp cha Animal:
virtual void Eat() {std::cout << "Cat: Eat\n";};
void Run() {std::cout << "Cat: Run\n";};
Trong hàm main.cpp:
Animal *obj;//obj là con trỏ có KIỂU Animal và trỏ tới ĐỐI TƯỢNG Cat
obj = new Cat();
obj->Eat();//--> Cat:Eat
obj->Run();//--> Aniaml:Run
//Animal.h
#pragma once
class Animal
{
public:
virtual void
Eat();
void Run();
};
//Animail.cpp
#include "Animal.h"
#include <iostream>
void Animal::Eat()
{
std::cout << "Animal:
Eat\n";
}
void Animal::Run()
{
std::cout << "Animal:
Run\n";
}
//Cat.h
#pragma once
#include "animal.h"
class Cat : public Animal
{
public:
void Eat();
void Run();
};
//Cat.cpp
#include "Cat.h"
#include <iostream>
void Cat::Eat()//overiding lai ham Eat cua lop cha Animal
{
std::cout << "Cat:
Eat\n";
}
void Cat::Run()//overiding lai ham Eat cua lop cha Animal
{
std::cout << "Cat:
Run\n";
}
//main.cpp
#include <iostream>
#include "Animal.h"
#include "Cat.h"
void main()
{
printf("1. Doi tuong kieu con
tro------\n");
Animal *obj;
obj = new Cat();
obj->Eat();//--> Cat:Eat
obj->Run();//--> Aniaml:Run
printf("2. Doi tuong
obj1------\n");
Animal obj1;
obj1.Eat();//Animal:Eat
obj1.Run();//Animal:Run
printf("3. Doi tuong
obj2------\n");
Cat obj2;
obj2.Eat(); //--> Cat:Eat
obj2.Run(); //--> Cat:Eat
system("pause");
}
#include <iostream>
using namespace
std;
class A{
public:
virtual void HienThi(){
cout
<< "A:hienthi\n" <<
endl;
};
void Todo(){
cout
<< "A:Todo\n" << endl;
}
};
class B: public
A{
public:
void Hienthi(){
cout
<< "B:hienthi\n" <<
endl;
};
void Todo(){
cout
<< "B:Todo\n" << endl;
}
};
class C: public
B{
public:
void Hienthi(){
cout
<< "C:hienthi\n" <<
endl;
};
void Todo(){
cout
<< "C:Todo\n" << endl;
}
};
class D: public
A{
public:
void hienthi(){
cout
<< "D:hienthi\n" <<
endl;
};
void Todo(){
cout
<< "D:Todo\n" << endl;
}
};
void main()
{
A *p;//Khởi tạo p là con trỏ có kiểu A
A a; //a là biến đối tượng kiểu A
B b; //b là biến đối tượng kiểu B
C c; //c là biến đối tượng kiểu C
D d; //d là biến đối tượng kiểu D
//1
p = &a; //p trỏ tới đối
tượng a của lớp A
p->HienThi();//gọi tới hàm A:hienthi()
p->Todo(); //gọi tới hàm
A:Todo()
//2
p = &b; //p trỏ tới đối
tượng b của lớp B
p->HienThi();//gọi tới hàm B:hienthi()
p->Todo(); //gọi tới hàm
A:Todo()
//3
p = &c; //p trỏ tới đối
tượng c của lớp C
p->HienThi();//gọi tới hàm C:hienthi()
p->Todo(); //gọi tới hàm
A:Todo()
//4
p = &d; //p trỏ tới đối
tượng d của lớp D
p->HienThi();//gọi tới hàm D:hienthi()
p->Todo(); //gọi tới hàm
A:Todo()
getchar();
}
9. Overriding vs. Overloading
10. Operator overloading
11.Class’ static member