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 (constX©_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 namespacestd;
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 namespacestd;
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 namespacestd;
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(){
deleteage;
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 namespacestd;
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 voidEat();
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 namespacestd;
class A{
public:
virtual void HienThi(){
cout << "A:hienthi\n" << endl;
};
void Todo(){
cout << "A:Todo\n" << endl;
}
};
class B: publicA{
public:
void Hienthi(){
cout << "B:hienthi\n" << endl;
};
void Todo(){
cout << "B:Todo\n" << endl;
}
};
class C: publicB{
public:
void Hienthi(){
cout << "C:hienthi\n" << endl;
};
void Todo(){
cout << "C:Todo\n" << endl;
}
};
class D: publicA{
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
0 nhận xét: