Vấn đề về hàm Destructor

Em có một bài tập về phần chuỗi. Đề bài yêu cầu xây dựng lớp CString để biểu diễn khái niệm chuỗi kí tự. Khai báo các phương thức thiết lập, hủy bỏ, các hàm thành phần và các phép toán cần thiết.
Vấn đề của em là tại hàm Destructor, máy báo lỗi “Debug Assertion Failed …”
Theo như em chạy debug thử thì thấy trước khi “return count” trong hàm strlen, chương trình đã gọi hàm Destructor để hủy đối tượng, nên sau khi return giá trị, chuỗi đã bi xóa. Sau khi kết thúc hàm strcmp, đối tượng a bị hủy lần thứ 2 nên báo lỗi.
Em cũng đã tìm hiểu trên các diễn đàn về vấn đề này nhưng chưa tìm ra cách khắc phục, chỉ có cách xóa hàm Destructor đi.
Mong anh chị hay bạn nào biết có thể khắc phục giúp em vấn đề này!
đây là phần khai báo lớp

class CString
{
private:
char *Str;
public:
CString();
~CString();
CString operator + (CString);
CString operator = (CString);
bool operator == (CString);
bool operator > (CString);
bool operator < (CString);
friend istream& operator >> (istream &is, CString &p);
friend ostream& operator << (ostream &os, CString p);
friend int strcmp(const CString, const CString);
friend int strlen(const CString);
friend CString strcpy(CString, const CString);
friend CString strcat(CString, const CString);
};

đây là khai báo các hàm thành phần

//Constructor
CString::CString()
{
Str = NULL;
}
CString::~CString()
{
cout << "Da huy" << endl;
//delete[] Str;
Str = NULL;
}
//Ham noi 2 CString
CString CString::operator + (CString b)
{
return strcat(*this, b);
}
//Overload toan tu =
CString CString::operator = (CString b)
{
Str = new char[strlen(b)];
strcpy(Str, b.Str);
return *this;
}
bool CString::operator > (CString b)
{
return strcmp(*this, b) > 0;
}
bool CString::operator < (CString b)
{
return strcmp(*this, b) < 0;
}
istream& operator >> (istream &is, CString &p)
{
p.Str = new char[100];
is.getline(p.Str, 100);
return is;
}
ostream& operator << (ostream &os, CString p)
{
os << p.Str;
return os;
}

đây là hàm main và các hàm friend của lớp
(báo lỗi ngay tại hàm strcmp bên dưới)

//Viet lai(overload) cac ham co san trong thu vien string.h cho lop CString
int strcmp(const CString a, const CString b)
{
int i = 0;
int j = min(strlen(a), strlen(b));
while(a.Str[i++] == b.Str[i++] && i <= j){}
return a.Str[--i] - b.Str[--i];          //Báo lỗi ở đây
}
int strlen(const CString a)
{
int count = 0;
while(a.Str[count++] != NULL);
return --count;
}
CString strcpy(CString dest, const CString source)
{
int i = 0;
while(source.Str* != NULL)
dest.Str* = source.Str[i++];
return dest;
}
CString strcat(CString dest, const CString source)
{
int i = strlen(dest);
int j = 0;
while(source.Str[j] != NULL)
dest.Str[i++] = source.Str[j++];
dest.Str* = NULL;
return dest;
}
void main()
{
CString a;
cin >> a;
CString b;
cin >> b;
if(a > b)
cout << ">" << endl;
else if(a < b)
cout << "<" << endl;
else
cout << "=" << endl;
cout << a + b << endl;
}

trong chương trình em đã định nghĩa lại các hàm strlen, strcmp, strcpy, strcat cho kiểu dữ liệu CString

Chúng tôi quan ngại sâu sắc về việc bạn không nói rõ HÀNG CODE NÀO BỊ LỖI. Đây là hành động mang tính thách đố người đọc và không có ích cho quá trình debug cũng như đi ngược lại nội quy bạn à.

[QUOTE=sinhvien.uit;279675]Chúng tôi quan ngại sâu sắc về việc bạn không nói rõ HÀNG CODE NÀO BỊ LỖI. Đây là hành động mang tính thách đố người đọc và không có ích cho quá trình debug cũng như đi ngược lại nội quy bạn à.[/QUOTE]

Vâng quả là em có sai sót
em đã sửa lại trong chương trình.
Em xin cảm ơn

Em làm sao chạy được lệnh này trong hàm main hay thế. Tới đây là lỗi biên dịch rồi làm gì chạy được hàm strcmp của em tự viết nữa mà có lỗi runtime
[QUOTE=13520747;279671]
(báo lỗi ngay tại hàm strcmp bên dưới)

//Viet lai(overload) cac ham co san trong thu vien string.h cho lop CString
void main()
{
	CString a;
	cin >> a;
	CString b;
	cin >> b;
	
[B][SIZE=5]	if(a > b)[/SIZE][/B]
		cout << ">" << endl;
	else if(a < b)
		cout << "<" << endl;
	else
		cout << "=" << endl;

	cout << a + b << endl;
}

trong chương trình em đã định nghĩa lại các hàm strlen, strcmp, strcpy, strcat cho kiểu dữ liệu CString[/QUOTE]

Code bạn nhiều quá mình đọc ko nỗi . Mình làm thế này và thấy hết lỗi . Thử xem sao nhé

CString::~CString()
{

	if(Str!=NULL)
	       delete []Str;
	Str = NULL;
}

Về vấn đề hàm hủy: Bạn ko đưa hết code nên mình đoán thế này: có vẻ bạn đã sai trong cấp phát bộ nhớ, cụ thể có 1 chỗ bạn đưa ra là khi overload toán tử “=”
bạn chỉ cấp phát cho Str của Object cần gán với số ô nhớ đúng bằng chiều dài của Str của Object copy. => Như vậy bạn đã bỏ qua ko cấp phát cho ký tự NULL (mặc định sau 1 chuỗi) -> việc này dẫn tới khi giải phóng Str sẽ gây ra lỗi như bạn nói (khi hàm chạy xong, các biến local sẽ tự động bị hủy, nếu là object thì dĩ nhiên hàm hủy của nó được gọi)

nói rộng hơn thì:
char*a = new char[3];
strcpy(a,“str”); //hàm này nó copy cả ký tự NULL, như vậy là có 4 ký tự đc copy
delete a;// => lỗi

giải pháp của mình: Str = new char[strlen(b) + 1]; trong tất cả các cấp phát (hàm tạo copy, …) chú ý với cái hàm tạo copy, vì tham số của hàm sẽ đc truyền bởi loại hàm tạo này khi gọi hàm

Bạn cho mình hỏi chỗ này

istream& operator >> (istream &is, CString &p)
{
	p.Str = new char[100];
	is.getline(p.Str, 100);
	return is;
}

sao bạn biết là 100, lỡ người ta dùng ít hơn hay nhiều hơn thì sao ?.

Nhiều giả thuyết thật. Nói chung chủ thớt không đưa đủ code mà cũng không nói rõ chạy với dữ liệu gì thì lỗi nên khó mà đoán. Tui chỉ có một thắc mắc tại sao các bạn phải code nhiều thế này trong khi chúng ta có thể dùng chiêu


#include <string>
class CStrings : public string { };

[QUOTE=truonganpn;279731]Nhiều giả thuyết thật. Nói chung chủ thớt không đưa đủ code mà cũng không nói rõ chạy với dữ liệu gì thì lỗi nên khó mà đoán. Tui chỉ có một thắc mắc tại sao các bạn phải code nhiều thế này trong khi chúng ta có thể dùng chiêu


#include <string>
class CStrings : public string { };

[/QUOTE]

đi thi em làm cách này thầy chấm điển sao vậy :confused:

Chiêu gì đây thầy :slight_smile:

[QUOTE=truonganpn;279731]Nhiều giả thuyết thật. Nói chung chủ thớt không đưa đủ code mà cũng không nói rõ chạy với dữ liệu gì thì lỗi nên khó mà đoán. Tui chỉ có một thắc mắc tại sao các bạn phải code nhiều thế này trong khi chúng ta có thể dùng chiêu


#include <string>
class CStrings : public string { };

[/QUOTE]

[QUOTE=truonganpn;279731]Nhiều giả thuyết thật. Nói chung chủ thớt không đưa đủ code mà cũng không nói rõ chạy với dữ liệu gì thì lỗi nên khó mà đoán. Tui chỉ có một thắc mắc tại sao các bạn phải code nhiều thế này trong khi chúng ta có thể dùng chiêu


#include <string>
class CStrings : public string { };

[/QUOTE]

Dùng chiêu xong có bị ăn 0 hok ạ. Chắc là tự làm để hiểu về OOP thôi,

[QUOTE=12520167;279737]đi thi em làm cách này thầy chấm điển sao vậy :confused:[/QUOTE]

Thường dạng bài này tui yêu cầu cài lớp đa thức chứ không cài lớp string. Các bạn cài lớp strings thường nản giữa chừng đặc biệt là khi thư viện chuẩn đã có sẵn và cài khá tốt. Nếu các bạn muốn làm lại cho hiểu thì phải thật kiên nhẫn, và nên cài các hàm thành phần, các toán tử giống y chang như lớp sting của thư viện, đừng chế thêm làm gì. Ví dụ như trong lớp string lại có cái hàm friend strcmp là thấy sai rồi vì overload các toán tử so sánh cho nó sẽ tiện hơn nhiều.

Các bạn có thể mở mã nguồn các thư viện C++ chuẩn ra để xem cách họ làm nếu cần: http://gcc.gnu.org/libstdc++/

[QUOTE=13520549;279743]Chiêu gì đây thầy :)[/QUOTE]

Học tới kế thừa là biết chiêu này.

[QUOTE=truonganpn;279684]Em làm sao chạy được lệnh này trong hàm main hay thế. Tới đây là lỗi biên dịch rồi làm gì chạy được hàm strcmp của em tự viết nữa mà có lỗi runtime[/QUOTE]
do lúc em post lên đây sơ ý xóa mấy hàm so sánh mà quên để ý là trong hàm main có sử dụng :3
dĩ nhiên là trong project của em vẫn có đủ những hàm đó nên em không nhận ra
cảm ơn thầy!

[QUOTE=13520549;279711]Code bạn nhiều quá mình đọc ko nỗi . Mình làm thế này và thấy hết lỗi . Thử xem sao nhé

CString::~CString()
{

	if(Str!=NULL)
	       delete []Str;
	Str = NULL;
}

[/QUOTE]
mình cũng đã thử cách này trước đó nhưng không được bạn ơi

[QUOTE=12520167;279730]Bạn cho mình hỏi chỗ này

istream& operator >> (istream &is, CString &p)
{
	p.Str = new char[100];
	is.getline(p.Str, 100);
	return is;
}

sao bạn biết là 100, lỡ người ta dùng ít hơn hay nhiều hơn thì sao ?.[/QUOTE]
chỗ này tạm thời em chưa quan tâm đến :smiley:
nhưng anh có giải pháp gì cho việc cấp phát động chuỗi kí tự không ạ?

[QUOTE=truonganpn;279745]Ví dụ như trong lớp string lại có cái hàm friend strcmp là thấy sai rồi vì overload các toán tử so sánh cho nó sẽ tiện hơn nhiều.

[/QUOTE]
Nếu như cài đặt là hàm thành phần thì em phải viết là a.strcmp(b) hay a.strlen(), … Như vậy đâu có giống với cấu trúc các hàm có sẵn đâu thầy?

[QUOTE=truonganpn;279731]Nhiều giả thuyết thật. Nói chung chủ thớt không đưa đủ code mà cũng không nói rõ chạy với dữ liệu gì thì lỗi nên khó mà đoán. Tui chỉ có một thắc mắc tại sao các bạn phải code nhiều thế này trong khi chúng ta có thể dùng chiêu


#include <string>
class CStrings : public string { };

[/QUOTE]
siêu bựa dành cho thi cử

[QUOTE=13520747;279751]mình cũng đã thử cách này trước đó nhưng không được bạn ơi

chỗ này tạm thời em chưa quan tâm đến :smiley:
nhưng anh có giải pháp gì cho việc cấp phát động chuỗi kí tự không ạ?[/QUOTE]
đã thế thì đỡ lùng xùng thì cứ destructor ta chứ cho string[0]=‘\0’ là coi như tạm biệt em.thế thôi
:brick:

Cảm ơn thầy và các bạn, em đã tìm được cách khắc phục :smiley:
đó là ứ chơi với thằng strcpy nữa :3
mặc dù đã cấp phát vùng nhớ trước cho nó và kiểm tra thông qua quá trình debug chả thấy lỗi gì chỗ này
cách khắc phục: xài luôn hàm char* strdup(const char *source) là xong, hàm sẽ tự tạo vùng nhớ vừa đủ để chứa chuỗi source, đỡ phải lăn tăn nhiều :smiley:

[QUOTE=13520747;279751]mình cũng đã thử cách này trước đó nhưng không được bạn ơi

chỗ này tạm thời em chưa quan tâm đến :smiley:
nhưng anh có giải pháp gì cho việc cấp phát động chuỗi kí tự không ạ?[/QUOTE]

Nếu không quan tâm đến cấp phát thì em khai báo luôn cái mảng tĩnh 100 phần tử đi và khỏi cần destructor nữa.
[QUOTE=13520747;279753]Nếu như cài đặt là hàm thành phần thì em phải viết là a.strcmp(b) hay a.strlen(), … Như vậy đâu có giống với cấu trúc các hàm có sẵn đâu thầy?[/QUOTE]

Em nên phân biệt thư viện string của C++ và thư viện string.h của C chuẩn. Code các hàm tương tự như trong string.h cho một lớp tương tự như string của C++ là một ý tưởng vô cùng kỳ quặc

[QUOTE=13520797;279764]đã thế thì đỡ lùng xùng thì cứ destructor ta chứ cho string[0]=‘\0’ là coi như tạm biệt em.thế thôi
:brick:[/QUOTE]

Tư tưởng thật là đồi bại. Thà dùng mảng tĩnh ngay từ đầu còn dễ coi hơn cách này.

[QUOTE=truonganpn;279772]Nếu không quan tâm đến cấp phát thì em khai báo luôn cái mảng tĩnh 100 phần tử đi và khỏi cần destructor nữa. [/QUOTE]
Dĩ nhiên là em sẽ quan tâm tới nó sau khi tìm được solution cho vấn đề này :smiley:
Trước đây em hay làm theo cách: cấp phát thật nhiều cho người dùng nhập vào, sau đó tạo 1 chuỗi mới vừa đủ và copy qua, cuối cùng xóa cái chuỗi ban đầu đi
Làm vậy thì khá là dài dòng rắc rối tốn thời gian, nhưng em chưa nghĩ ra cách nào hợp lý và hiệu quả hơn :go:

Em nên phân biệt thư viện string của C++ và thư viện string.h của C chuẩn. Code các hàm tương tự như trong string.h cho một lớp tương tự như string của C++ là một ý tưởng vô cùng kỳ quặc

đúng là em có chút nhầm lẫn qua lại giữa hai thư viện này :confuse: