题目描述
定义一个电话号码类CTelNumber,包含1个字符指针数据成员 。成员函数包含:构造、析构、打印、拷贝构造、判断电话号码合法性函数。
字符指针是用于动态创建一个字符数组,然后保存外来输入的电话号码
构造函数的功能是为对象设置键盘输入的7位电话号码,
拷贝构造函数的功能是用原来7位号码的对象升位为8位号码对象,也就是说拷贝构造的对象是源对象的升级.电话升位的规则是原2、3、4开头的电话号码前面加8,原5、6、7、8开头的前面加2。
注意:合法的电话号码:1、长度为7位;2、电话号码的字符全部是数字字符;3、第一个字符只能是以下字符:2、3、4、5、6、7、8。与上述情况不符的输入均为非法
输入
测试数据的组数 t
第一个7位号码
第二个7位号码
......
输出
第一个号码升位后的号码
第二个号码升位后的号码
......
代码(未优化)
#include <iostream>
#include <cstring>
using namespace std;
class CTeINumber {
public:
CTeINumber(char *a) {
tel = new char[strlen(a) + 1];
strcpy(tel, a);
}
~CTeINumber() {
delete[] tel;
}
void print() {
if (isLegal()) {
cout << tel << endl;
} else {
cout << "Illegal phone number" << endl;
}
}
CTeINumber(const CTeINumber &p) {
tel = new char[9];
strcpy(tel + 1, p.tel);
if (p.tel[0] == '2' || p.tel[0] == '3' || p.tel[0] == '4') {
tel[0] = '8';
} else {
tel[0] = '2';
}
tel[8] = '\0';
}
bool isLegal() const {
if (strlen(tel) != 8) {
return false;
}
for (int i = 0; i < 8; i++) {
if (tel[i] < '0' || tel[i] > '9') {
return false;
}
}
if (tel[1] == '0' || tel[1] == '1' || tel[1] == '9') {
return false;
}
return true;
}
protected:
char *tel;
};
int main() {
int t;
cin >> t;
while (t--) {
char a[10];
cin >> a;
CTeINumber p1(a);
CTeINumber p2 = p1;
p2.print();//islegal是在拷贝后的,已经加一了
}
return 0;
}
对深拷贝的理解和运用
{
如果不重新定义开一个动态空间,并用strcpy,(而不是‘=’ —— 错误的)
那类的两个对象中的指针会指向同一个堆区空间,因此析构函数会析构两回这个同一堆区空间而崩溃
}
- 当要拷贝的对象有被动态分配内存时——存在char *指针,要进行深拷贝;
- 即在拷贝函数中,重新开一个动态内存(new),然后,通过strcpy来复制被拷贝对象的内容到这个新开的动态内存中;
- 最后,再把这个动态内存通过等号赋给当前类对象的动态内存中
//直接对当前对象的指针进行分配内存
CTeINumber(const CTeINumber &p)
{
tel = new char[9];
strcpy(tel + 1, p.tel);
if (p.tel[0] == '2' || p.tel[0] == '3' || p.tel[0] == '4')
{
tel[0] = '8';
}
else
{
tel[0] = '2';
}
tel[8] = '\0';
}
//在当前进行定义一个新的指针来分配内存,再进行等号=(赋值,指向地址)
CTeINumber(const CTeINumber &p)
{
char* tel1 = new char[9];
strcpy(tel1 + 1, p.tel);
tel = tel1;
if (p.tel[0] == '2' || p.tel[0] == '3' || p.tel[0] == '4')
{
tel[0] = '8';
}
else
{
tel[0] = '2';
}
tel[8] = '\0';
}
对拷贝函数的参数加个&的原因:
引用,就不用在调用参数时再次进行类的拷贝了(在作为函数的参数时,会被拷贝,但引用就不需要了)
对于深拷贝char * 和string的区别
string是可以直接用=,因为string已经做好了其他方面的准备,不会出现上面对于指针的问题
#include <iostream>
using namespace std;
class p {
string *a; // 指向动态分配的字符串数组
public:
p(string *a1) : a(new string[10]) { // 构造函数
for (int i = 0; i < 10; ++i) {
a[i] = a1[i];
}
}
p(const p &p1) : a(new string[10]) { // 拷贝构造函数
for (int i = 0; i < 10; ++i) {
a[i] = p1.a[i];
}
}
~p() { // 析构函数
delete[] a;
}
};
int main() {
string b = "sdfqc";
string *a = new string[10]; // 动态分配一个字符串数组
for (int i = 0; i < 10; ++i) {
a[i] = b;
}
p q(a); // 使用a初始化对象q
p q1(q); // 使用拷贝构造函数初始化q1
delete[] a; // 释放动态分配的内存
return 0;
}
注:当char*赋值给string时,一定要判空(否则会造成程序崩溃):
//会报错代码
#include<iostream>
using namespace std;
int main()
{
char *p = NULL;
std::string strP = p; // string不接受NULL赋值
delete[] p;
}
//预先判空代码
#include<iostream>
using namespace std;
int main()
{
char *p = NULL;
if (p == NULL)
{
return;
}
std::string strP = p;
}
//当然,对于第一个有一些编译器会显示成功,但最好写第二个代码