目录:
人、学生和教师
公有成员、保护成员和私有成员
公有继承、保护继承和私有继承
基类子对象的隐式构造
基类子对象的显式构造
子类对象的构造过程
断子绝孙的类
delete指向子类对象的基类指针
子类对象的析构过程
缺省全拷贝
自定义局部拷贝
自定义全拷贝
缺省全赋值
自定义局部赋值
自定义全赋值
1 人、学生和教师
1.1 问题
定义三个类,它们是类Human,类Student和类Teacher,其中,类Human是类Student和类Teacher的基类。在类Human中,包含两个数据成员,一个是类string的对象m_name用于存储姓名,另一个是整型变量m_age,用于存储年龄。在类Human中,还包含六个函数成员,它们是m_name的设置、获取函数,m_age的设置、获取函数,eat函数和sleep函数。类Student继承自类Human,自己又新增了一个成员变量m_no,用于存储学号,新增了三个函数成员,它们是m_no的设置、获取函数和learn函数。类Teacher也继承自类Human,自己又新增了一个成员变量m_course,用于存储其所教授的课程,新增了三个函数成员,它们是m_course的设置、获取函数和teach函数。
1.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:人、学生和教师
代码如下:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int eat;
int m_no;
public:
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
//std::cout << m_name << “学” << course << std::endl; // 错误
std::cout << name () << “学” << course << std::endl;
}
};
class Teacher : public Human
{
private:
std::string m_course;
public:
void setCourse (std::string const& course)
{
m_course = course;
}
std::string const& course (void) const
{
return m_course;
}
void teach () const
{
std::cout << name () << “教《” << m_course << “》课程” << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student;
Human* phuman = &student;
//Student* pstudent = phuman; // 错误
Student* pstudent = static_cast<Student*> (phuman);_
Human& rhuman = student;
//Student& rstudent = rhuman; // 错误
Student& rstudent = static_cast<Student&> (rhuman);
//student.eat("饺子");
student.Human::eat("饺子");
student.sleep(6);
return 0;
}
上述代码中,以下代码:
Student student;
Human* phuman = &student;
和以下代码:
Human& rhuman = student;
是合法的,子类类型Student的指针或引用总可以被隐式地转换为其基类类型Human的指针或引用,这种操作性缩小的类型转换,在编译器看来是安全的。
上述代码中,以下代码:
//Student* pstudent = phuman; // 错误
和以下代码:
//Student& rstudent = rhuman; // 错误
是非法的。基类类型Human的指针或引用不可以被隐式地转换为其子类类型Student的指针或引用,这种操作性扩大的类型转换,在编译器看来是危险的。如果一定要转换,可以使用静态类型转换。如以下代码:
Student* pstudent = static_cast<Student*> (phuman);_
或
Student& rstudent = static_cast<Student&> (rhuman);
上述代码中,以下代码:
student.sleep(6);
在子类中或通过子类,可以直接访问基类的所有公有和保护成员,就如同它们是在子类中声明的一样。
上述代码中,以下代码:
void learn (std::string const& course) const
{
//std::cout << m_name << "学" << course << std::endl; // 错误
std::cout << name () << "学" << course << std::endl;
}
基类的私有成员m_name在子类Student中虽然存在却是不能直接使用的,故无法直接访问。
上述代码中,以下代码:
class Student : public Human
{
private:
int eat;
int m_no;
尽管基类Human的公有和保护成员在子类Student中直接可见,但仍然可以在子类中重新定义这些名字,如eat,在基类Human中是成员函数名,在子类Student中可以重新定义成成员变量名,需要注意的是子类中的名字会隐藏所有基类中的同名定义。如下代码所示:
//student.eat("饺子");
在子类Student中,eat被定义成私有的成员变量,这样eat会隐藏从基类继承过来的成员函数eat,如此调用是非法的。应改成:
student.Human::eat("饺子");
如果需要在子类Student中或通过子类访问一个在基类Human中定义却为子类所隐藏的名字eat,可以借助作用域限定操作符“::”实现。
1.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int eat;
int m_no;
public:
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
//std::cout << m_name << “学” << course << std::endl; // 错误
std::cout << name () << “学” << course << std::endl;
}
};
class Teacher : public Human
{
private:
std::string m_course;
public:
void setCourse (std::string const& course)
{
m_course = course;
}
std::string const& course (void) const
{
return m_course;
}
void teach () const
{
std::cout << name () << “教《” << m_course << “》课程” << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student;
Human* phuman = &student;
//Student* pstudent = phuman; // 错误
Student* pstudent = static_cast<Student*> (phuman);_
Human& rhuman = student;
//Student& rstudent = rhuman; // 错误
Student& rstudent = static_cast<Student&> (rhuman);
//student.eat("饺子");
student.Human::eat("饺子");
student.sleep(6);
return 0;
}
2 公有成员、保护成员和私有成员
2.1 问题
访问控制限定符规定了一个类的特定成员,是否具有被从类的内部、类的子类,以及类的外部进行访问的能力。说明见图-1。
图-1
2.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:公有成员、保护成员和私有成员
代码如下:
#include
class Base
{
private:
int m_private;
protected:
int m_protected;
public:
int m_public;
void show (void) const
{
std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
friend void print (Base const& base);
};
class Derive : public Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
void print (Base const& base)
{
std::cout << base.m_private << std::endl;
std::cout << base.m_protected << std::endl;
std::cout << base.m_public << std::endl;
}
int main(int argc, const char * argv[])
{
Base base;
// base.m_private = 10;
// base.m_protected = 20;
base.m_public = 30;
print (base);
return 0;
}
上述代码中,以下代码:
class Base
{
private:
int m_private;
protected:
int m_protected;
public:
int m_public;
在类Base中定义了一个私有的成员变量m_private,一个保护的成员变量m_protected和一个公有的成员变量m_public。
上述代码中,以下代码:
class Base
{
private:
int m_private;
protected:
int m_protected;
public:
int m_public;
void show (void) const
{
std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
在类Base内,成员函数show可以访问类Base的私有的、保护的和公有的成员变量。
上述代码中,以下代码:
class Derive : public Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
在类Base的子类Derive中的成员函数show只可以访问类Base的保护的和公有的成员变量,不能访问私有的成员变量。
上述代码中,以下代码:
void print (Base const& base)
{
std::cout << base.m_private << std::endl;
std::cout << base.m_protected << std::endl;
std::cout << base.m_public << std::endl;
}
是一个全局函数,但该函数在类Base中被声明为友元,如下语句所示:
friend void print (Base const& base);
所以,全局函数print可以访问类Base的私有的、保护的和公有的成员变量。
上述代码中,以下代码:
int main(int argc, const char * argv[])
{
Base base;
// base.m_private = 10;
// base.m_protected = 20;
base.m_public = 30;
print (base);
return 0;
}
在类外定义了类Base的对象base,该对象只能访问类Base的共有的成员,不能访问私有的和保护的成员。
2.3 完整代码
本案例的完整代码如下所示:
#include
class Base
{
private:
int m_private;
protected:
int m_protected;
public:
int m_public;
void show (void) const
{
std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
friend void print (Base const& base);
};
class Derive : public Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
void print (Base const& base)
{
std::cout << base.m_private << std::endl;
std::cout << base.m_protected << std::endl;
std::cout << base.m_public << std::endl;
}
int main(int argc, const char * argv[])
{
Base base;
// base.m_private = 10;
// base.m_protected = 20;
base.m_public = 30;
print (base);
return 0;
}
3 公有继承、保护继承和私有继承
3.1 问题
基类中的公有、保护和私有成员,在其公有、保护和私有子类中的访问控制属性,会因继承方式而异。说明见图-2。
图-2
3.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:公有继承、保护继承和私有继承
代码如下:
#include
class Base
{
private:
int m_private;
protected:
int m_protected;
public:
int m_public;
};
class Public : public Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
class Protected : protected Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
class Private : private Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
int main(int argc, const char * argv[])
{
Base base;
// base.m_private = 10;
// base.m_protected = 20;
base.m_public = 30;
return 0;
}
上述代码中,以下代码:
class Base
{
private:
int m_private;
protected:
int m_protected;
public:
int m_public;
};
定义了一个类Base,在该类中定义了一个私有的成员变量m_private,一个保护的成员变量m_protected和一个公有的成员变量m_public。
上述代码中,以下代码:
class Public : public Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
定义了类Base的公有继承子类Public,在该类中,基类Base的私有成员m_private在类Public中变成了不可访问的成员,保护的成员变量m_protected仍然是保护的,公有的成员变量m_public仍然是公有的。
上述代码中,以下代码:
class Protected : protected Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
定义了类Base的保护继承子类Protected,在该类中,基类Base的私有成员m_private在类Protected中变成了不可访问的成员,保护的成员变量m_protected和公有的成员变量m_public都变成了保护的。
上述代码中,以下代码:
class Private : private Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
定义了类Base的私有继承子类Private,在该类中,基类Base的私有成员m_private在类Private中变成了不可访问的成员,保护的成员变量m_protected和公有的成员变量m_public都变成了私有的。
3.3 完整代码
本案例的完整代码如下所示:
#include
class Base
{
private:
int m_private;
protected:
int m_protected;
public:
int m_public;
};
class Public : public Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
class Protected : protected Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
class Private : private Base
{
public:
void show (void) const
{
//std::cout << m_private << std::endl;
std::cout << m_protected << std::endl;
std::cout << m_public << std::endl;
}
};
int main(int argc, const char * argv[])
{
Base base;
// base.m_private = 10;
// base.m_protected = 20;
base.m_public = 30;
return 0;
}
4 基类子对象的隐式构造
4.1 问题
如果子类的构造函数没有显式指明其基类部分的构造方式,那么编译器会选择其基类的缺省构造函数,构造该子类对象中的基类子对象,但是请注意,只有在为基类显式提供一个无参构造函数,或者不提供任何构造函数(系统会提供一个缺省的无参构造函数)的情况下,基类才拥有无参构造函数。
4.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:基类子对象的隐式构造
代码如下:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
public:
Student (const int& no) : m_no(no)
{
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student(123456);
return 0;
}
上述代码中,以下代码:
Human (std::string const& name = "", const int& age = 0) : m_name(name), m_age(age)
{
}
为基类Human显式提供一个带参构造函数,但由于该构造函数的两个形参均带有缺省参数,所以也可以将其当做无参构造函数来使用。
上述代码中,以下代码:
Student (const int& no) : m_no(no)
{
}
为子类Student显示的提供了一个带参构造函数,但由于子类Student的构造函数没有显式指明其基类Human部分的构造方式,那么编译器会选择其基类的缺省构造函数,构造该子类对象中的基类子对象。如以下主程序中的代码:
Student student(123456);
定义了一个子类Student的对象student,首先为student中继承的基类部分成员分配空间,分配完成后,就会调用基类的构造函数进行初始化,然后再分配子类中新添加的成员变量的存储空间,分配完成后,才会调用子类的构造函数进行初始化。
4.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
public:
Student (const int& no) : m_no(no)
{
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student(123456);
return 0;
}
5 基类子对象的显式构造
5.1 问题
子类的构造函数可以在初始化表中显式指明其基类部分的构造方式,即通过其基类的特定构造函数,构造该子类对象中的基类子对象。
5.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:基类子对象的显式构造
代码如下:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
public:
Student (std::string const& name, const int& age, const int& no) : Human(name, age), m_no(no)
{
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456);
return 0;
}
上述代码中,以下代码:
Human (std::string const& name = "", const int& age = 0) : m_name(name), m_age(age)
{
}
为基类Human显式提供一个带参构造函数,但由于该构造函数的两个形参均带有缺省参数,所以也可以将其当做无参构造函数来使用。
上述代码中,以下代码:
Student (std::string const& name, const int& age, const int& no) : Human(name, age), m_no(no)
{
}
为子类Student显示的提供了一个带参构造函数,子类Student的构造函数可以在初始化表中显式指明其基类Human部分的构造方式,即通过其基类Human的特定构造函数,构造该子类对象中的基类子对象。如以下主程序中的代码:
Student student("张三", 20, 123456);
定义了一个子类Student的对象student,首先为student中继承的基类部分成员分配空间,分配完成后,就会从初始化表中找到并调用基类Human的构造函数进行初始化,然后再分配子类中新添加的成员变量的存储空间,分配完成后,才会调用子类Student的构造函数进行初始化。
5.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
public:
Student (std::string const& name, const int& age, const int& no) : Human(name, age), m_no(no)
{
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456);
return 0;
}
6 子类对象的构造过程
6.1 问题
子类的构造函数执行过程是这样的,首先,按照继承表中的顺序,依次调用各个基类的构造函数,构造子类对象中的基类子对象,然后,按照声明的顺序,依次调用各个类类型成员变量相应类型的构造函数,构造子类对象中的成员子对象,最后,执行子类构造函数体中的代码,完成整个构造过程。无论如何,子类的构造函数都一定会(显式或隐式地)调用其基类和类类型成员变量类型的构造函数。
6.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:子类对象的构造过程
代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
std::string m_address;
public:
Student (std::string const& name, const int& age, const int& no, std::string const& address) : Human(name, age), m_address(address), m_no(no)
{
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
return 0;
}
上述代码中,以下代码:
Human (std::string const& name = "", const int& age = 0) : m_name(name), m_age(age)
{
}
为基类Human显式提供一个带参构造函数,但由于该构造函数的两个形参均带有缺省参数,所以也可以将其当做无参构造函数来使用。
上述代码中,以下代码:
Student (std::string const& name, const int& age, const int& no, std::string const& address) : Human(name, age), m_address(address), m_no(no)
{
}
为子类Student显示的提供了一个带参构造函数,子类Student的构造函数可以在初始化表中显式指明其基类Human部分的构造方式,即通过其基类Human的特定构造函数,构造该子类对象中的基类子对象。同时子类Student的构造函数在初始化表中,显式指明类类型string成员变量m_address相应类型的构造函数,构造子类对象中的成员子对象m_address。如以下主程序中的代码:
Student student("张三", 20, 123456, "江苏南京市");
定义了一个子类Student的对象student,首先,按照继承表中的顺序,如下代码:
class Student : public Human
冒号后面的部分即为继承表的内容,当多重继承时,继承表中可以有多个基类,此时依次调用各个基类的构造函数,构造子类对象中的基类子对象。然后,按照声明的顺序,如下代码:
private:
int m_no;
std::string m_address;
声明的顺序就是定义数据成员的顺序,一个类中可能会有多个类类型的成员,此时依次调用各个类类型成员变量相应类型的构造函数,构造子类对象中的成员子对象。最后,执行子类构造函数体中的代码,完成整个构造过程。
6.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
std::string m_address;
public:
Student (std::string const& name, const int& age, const int& no, std::string const& address) : Human(name, age), m_address(address), m_no(no)
{
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
return 0;
}
7 断子绝孙的类
7.1 问题
与其它一些面向对象语言(比如Java)不同,C++语言并没有提供专门的方法来阻止一个类被继承。但如果把一个类的全部构造函数声明为私有,那么一旦从该类派生了子类,该子类将由于无法调用基类的构造函数而导致编译错误,因为基类的私有部分的内容在子类中是不可访问的。为了获得被阻断继承的基类本身的对象,可能需要为其提供专门用于创建对象的静态接口。
7.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:断子绝孙的类
代码如下所示:
#include
class User
{
private:
std::string m_name;
User (void)
{
}
User (std::string const& name) : m_name(name)
{
}
User (User const& user)
{
}
public:
static User* createInstance (std::string const& name)
{
return new User(name);
}
void show (void) const
{
std::cout << m_name << std::endl;
}
};
class UserVIP : public User
{
};
int main(int argc, const char * argv[])
{
//UserVIP vip;
User *user = User::createInstance("张三");
user->show();
return 0;
}
上述代码中,以下代码:
class User
{
private:
std::string m_name;
User (void)
{
}
User (std::string const& name) : m_name(name)
{
}
User (User const& user)
{
}
把类User的构造函数声明为私有的,则这些构造函数将不能在类外被使用。
上述代码中,以下代码:
class UserVIP : public User
{
};
定义了基类User的子类UserVIP,这是错误的。因为基类User中构造函数是私有成员,则被继承到子类UserVIP中这些构造函数将是不可访问的,那么子类的构造函数必须显示的或隐式的调用基类的构造函数,由于构造函数的不可访问性,造成不能访问。如下主程序中的语句:
//UserVIP vip;
就会出现语法错误。
上述代码中,以下代码:
static User* createInstance (std::string const& name)
{
return new User(name);
}
为了获得基类User本身的对象,则需要为其提供专门用于创建对象的静态接口。如下主程序中的语句:
User *user = User::createInstance("张三");
user->show();
创建类User的对象。
7.3 完整代码
本案例的完整代码如下所示:
#include
class User
{
private:
std::string m_name;
User (void)
{
}
User (std::string const& name) : m_name(name)
{
}
User (User const& user)
{
}
public:
static User* createInstance (std::string const& name)
{
return new User(name);
}
void show (void) const
{
std::cout << m_name << std::endl;
}
};
class UserVIP : public User
{
};
int main(int argc, const char * argv[])
{
//UserVIP vip;
User *user = User::createInstance("张三");
user->show();
return 0;
}
8 delete指向子类对象的基类指针
8.1 问题
对一个指向子类对象的基类指针使用delete运算符,实际被调用的将是基类的析构函数,该函数不会调用子类的析构函数,其所析构的仅仅是子类对象中的基类子对象,而子类的扩展部分极有可能因此而形成内存泄漏。
8.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:delete指向子类对象的基类指针
代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
~Human (void)
{
std::cout << “Human destructor” << std::endl;
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const int& no, const char* address) : m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
~Student (void)
{
delete [] m_address;
std::cout << “Student destructor” << std::endl;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Human *pHuman = new Student(123456, "江苏南京市");
delete pHuman;
return 0;
}
上述代码中,以下代码:
~Human (void)
{
std::cout << "Human destructor" << std::endl;
}
定义了类Human的析构函数,在该函数中,只是输出一句话,提示该函数被调用了。
上述代码中,以下代码:
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const int& no, const char* address) : m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
在子类Student中定义了一个字符指针m_address,并在构造函数中,为其分配存储空间。
上述代码中,以下代码:
~Student (void)
{
delete [] m_address;
std::cout << "Student destructor" << std::endl;
}
由于在子类Student的构造函数中给m_address分配了存储空间,所以在析构函数中需要将其释放。
上述代码中,以下代码:
Human *pHuman = new Student(123456, "江苏南京市");
定义了一个基类的指针pHuman,让它指向一个子类Student在堆上的对象。这是没有问题的。因为基类的指针可以指向子类的对象。但此时该指针只能访问子类中继承过来的基类的子对象,子类中自己扩展的内容该指针无法访问。这就造成了一个问题,如下主程序中的语句:
delete pHuman;
当用delete释放pHuman指向的对象时,只会调用子类中继承过来的基类部分的析构函数,释放子类中继承过来的基类部分,而不会调用子类的析构函数。如上所述,子类的析构函数要释放m_address指向的空间,不调用子类的析构函数,就意味着内存泄露。
8.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
~Human (void)
{
std::cout << “Human destructor” << std::endl;
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const int& no, const char* address) : m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
~Student (void)
{
delete [] m_address;
std::cout << “Student destructor” << std::endl;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Human *pHuman = new Student(123456, "江苏南京市");
delete pHuman;
return 0;
}
9 子类对象的析构过程
9.1 问题
子类的析构函数的执行过程是这样的。首先,执行子类析构函数体中的代码,析构子类的扩展部分,然后,按照声明的逆序,依次调用各个类类型成员变量相应类型的析构函数,析构子类对象中的成员子对象,最后,按照继承表的逆序,依次调用各个基类的析构函数,析构子类对象中的基类子对象,完成整个析构过程。无论如何,子类的析构函数都一定会隐式地调用其类类型成员变量类型和基类的析构函数。
9.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:子类对象的析构过程
代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
~Human (void)
{
std::cout << “Human destructor” << std::endl;
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
std::string m_address;
public:
Student (std::string const& name, const int& age, const int& no, std::string const& address) : Human(name, age), m_address(address), m_no(no)
{
}
~Student (void)
{
std::cout << “Student destructor” << std::endl;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student *pStudent = new Student("张三", 20, 123456, "江苏南京市");
delete pStudent;
return 0;
}
上述代码中,以下代码:
~Human (void)
{
std::cout << "Human destructor" << std::endl;
}
定义了类Human的析构函数,在该函数中,只是输出一句话,提示该函数被调用了。
上述代码中,以下代码:
~Student (void)
{
std::cout << "Student destructor" << std::endl;
}
定义了子类Student的析构函数,在该函数中,也只是输出一句话,提示该函数被调用了。
上述代码中,以下代码:
Student *pStudent = new Student("张三", 20, 123456, "江苏南京市");
定义了子类Student的指针pStudent,让它指向堆上的子类Student的对象。
上述代码中,以下代码:
delete pStudent;
释放子类Student的对象pStudent。此时由于堆上的子类Student的对象生命周期结束,会首先执行子类Student析构函数体中的代码,析构子类的扩展部分,然后,按照声明的逆序,如下代码:
private:
int m_no;
std::string m_address;
声明的顺序就是定义数据成员的顺序,一个类中可能会有多个类类型的成员,此时依逆序调用各个类类型成员变量相应类型的析构函数,析构子类对象中的成员子对象,最后,按照继承表的逆序,如下代码:
class Student : public Human
冒号后面的部分即为继承表的内容,当多重继承时,继承表中可以有多个基类,此时依逆序调用各个基类的析构函数,析构子类对象中的基类子对象,完成整个析构过程。
9.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
std::string m_name;
int m_age;
public:
Human (std::string const& name = “”, const int& age = 0) : m_name(name), m_age(age)
{
}
~Human (void)
{
std::cout << “Human destructor” << std::endl;
}
void setName (std::string const& name)
{
m_name = name;
}
void setAge (const int& age)
{
m_age = age;
}
std::string const& name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
std::string m_address;
public:
Student (std::string const& name, const int& age, const int& no, std::string const& address) : Human(name, age), m_address(address), m_no(no)
{
}
~Student (void)
{
std::cout << “Human destructor” << std::endl;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student *pStudent = new Student("张三", 20, 123456, "江苏南京市");
delete pStudent;
return 0;
}
10 缺省全拷贝
10.1 问题
如果子类没有定义拷贝构造函数,那么编译器为子类提供的缺省拷贝构造函数,会自动调用其基类的(自定义或缺省)拷贝构造函数,拷贝构造子类对象中的基类子对象。
10.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:缺省全拷贝
代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1 = student;
return 0;
}
上述代码中,没有定义基类Human和子类Student的拷贝构造函数,此时编译器会为它们各自添加一个缺省拷贝构造函数。值得注意的是,缺省拷贝构造函数在实现子类Student的对象复制时,如下代码所示:
Student student1 = student;
采用字节复制的方法。复制的结果是继承过来的基类子对象中的指针student1.m_name指向了指针student.m_name指向的空间,是student.m_name指针的浅拷贝,同样子类扩展的指针student1.m_address指向了student.m_address指向的空间,是student.m_address指针的浅拷贝。如果student对象先释放了,则student1.m_name与student1.m_address都将变成野指针。
10.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1 = student;
return 0;
}
11 自定义局部拷贝
11.1 问题
如果子类定义了拷贝构造函数,但没有显式指明以拷贝方式构造其基类部分,那么编译器会选择其基类的缺省构造函数,构造子类对象中的基类子对象
11.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:自定义局部拷贝
代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1 = student;
return 0;
}
上述代码中,以下代码:
Student (Student const& student) : m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
子类Student定义了拷贝构造函数,但没有显式指明以拷贝方式构造其基类部分,即在冒号后面的初始化列表中没有调用基类的拷贝构造函数,那么编译器会选择其基类的缺省构造函数,构造子类对象中的基类子对象。这样就造成主程序中的如下代码:
Student student1 = student;
在复制对象student时,对象student1中的继承过来的基类子对象调用缺省构造函数,这样,student1.m_name将为空,student1.m_age将是0,而子类扩展的指针student1.m_address由于自定义了拷贝构造函数,指向了自己的空间,没有指向student.m_address指向的空间,是student.m_address指针的深拷贝。即是student1没有把student的全部内容拷贝过来。
11.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1 = student;
return 0;
}
12 自定义全拷贝
12.1 问题
如果子类定义了拷贝构造函数,同时显式指明了其基类部分以拷贝方式构造,那么子类对象中的基类部分和扩展部分将一起被复制。
12.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:自定义全拷贝
代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
Human (Human const& human) : m_age(human.m_age)
{
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : Human(student), m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1 = student;
return 0;
}
上述代码中,以下代码:
Human (Human const& human) : m_age(human.m_age)
{
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
}
定义了基类Human的拷贝构造函数。
上述代码中,以下代码:
Student (Student const& student) : Human(student), m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
定义了子类Student的拷贝构造函数,同时在冒号后面的初始化列表中显式指明了其基类部分以拷贝方式构造,这样子类对象中的基类部分和扩展部分将一起被深拷贝。
12.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
Human (Human const& human) : m_age(human.m_age)
{
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : Human(student), m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1 = student;
return 0;
}
13 缺省全赋值
13.1 问题
如果子类没有定义拷贝赋值运算符函数,那么编译器为子类提供的缺省拷贝赋值运算符函数,会自动调用其基类的(自定义或缺省)拷贝赋值运算符函数,复制子类对象中的基类子对象。
13.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:缺省全赋值
代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1("李四", 25, 654321, "广东广州市");
student1 = student;
return 0;
}
上述代码中,没有定义基类Human和子类Student的拷贝赋值运算符函数,此时编译器会为它们各自添加一个缺省拷贝赋值运算符函数。值得注意的是,缺省拷贝赋值运算符函数在实现子类Student的对象复制时,如下代码所示:
student1 = student;
采用字节复制的方法。复制的结果是继承过来的基类子对象中的指针student1.m_name指向了指针student.m_name指向的空间,是student.m_name指针的浅拷贝,同样子类扩展的指针student1.m_address指向了student.m_address指向的空间,是student.m_address指针的浅拷贝。如果student对象先释放了,则student1.m_name与student1.m_address都将变成野指针。
13.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1("李四", 25, 654321, "广东广州市");
student1 = student;
return 0;
}
14 自定义局部赋值
14.1 问题
如果子类定义了拷贝赋值运算符函数,但没有显式调用其基类的拷贝赋值运算符函数,那么子类对象中的基类子对象将因得不到复制而保持原状。
14.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:自定义局部赋值
代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
void show (void) const
{
std::cout << “” << m_name << “,年龄:” << m_age << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
Student& operator= (Student const& student)
{
if (&student != this)
{
delete [] m_address;
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
m_no = student.m_no;
}
return *this;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
void show (void) const
{
Human::show();
std::cout << “学号:” << m_no << “,住址:” << m_address << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1("李四", 25, 654321, "广东广州市");
student1 = student;
student.show();
student1.show();
return 0;
}
上述代码中,以下代码:
Student& operator= (Student const& student)
{
if (&student != this)
{
delete [] m_address;
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
m_no = student.m_no;
}
return *this;
}
定义了子类Student的拷贝赋值运算符函数,但在函数体中没有显式调用其基类的拷贝赋值运算符函数,那么子类对象中的基类子对象将因得不到复制而保持原状。如执行主程序中的如下语句:
Student student("张三", 20, 123456, "江苏南京市");
Student student1("李四", 25, 654321, "广东广州市");
student1 = student;
student.show();
student1.show();
则可以看到类Student的对象student1中的基类子对象内容没变,姓名仍然是“李四”,年龄仍然是25,但对象student1中的扩展部分变成了对象student中的扩展部分,学号变成了123456,家庭住址变成了“江苏南京市”。
14.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setName (const char* name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
void show (void) const
{
std::cout << “” << m_name << “,年龄:” << m_age << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
Student& operator= (Student const& student)
{
if (&student != this)
{
delete [] m_address;
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
m_no = student.m_no;
}
return *this;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
void show (void) const
{
Human::show();
std::cout << “学号:” << m_no << “,住址:” << m_address << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1("李四", 25, 654321, "广东广州市");
student1 = student;
student.show();
student1.show();
return 0;
}
15 自定义全赋值
15.1 问题
如果子类定义了拷贝赋值运算符函数,同时显式调用了其基类的拷贝赋值运算符函数,那么子类对象中的基类部分和扩展部分将一起被复制。
15.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:自定义全赋值
代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
Human (Human const& human) : m_age(human.m_age)
{
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
}
Human& operator= (Human const& human)
{
if (&human != this)
{
delete [] m_name;
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
m_age = human.m_age;
}
return this;
}
void setName (const char name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
void show (void) const
{
std::cout << “” << m_name << “,年龄:” << m_age << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : Human(student), m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
Student& operator= (Student const& student)
{
if (&student != this)
{
Human::operator=(student);
delete [] m_address;
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
m_no = student.m_no;
}
return *this;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
void show (void) const
{
Human::show();
std::cout << “学号:” << m_no << “,住址:” << m_address << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student("张三", 20, 123456, "江苏南京市");
Student student1("李四", 25, 654321, "广东广州市");
student1 = student;
student.show();
student1.show();
return 0;
}
上述代码中,以下代码:
Human& operator= (Human const& human)
{
if (&human != this)
{
delete [] m_name;
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
m_age = human.m_age;
}
return *this;
}
定义了基类Human的拷贝赋值运算符函数。
上述代码中,以下代码:
Student& operator= (Student const& student)
{
if (&student != this)
{
Human::operator=(student);
delete [] m_address;
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
m_no = student.m_no;
}
return *this;
}
定义了子类Student的拷贝赋值运算符函数,由于在函数体中,使用如下语句:
Human::operator=(student);
显式调用其基类的拷贝赋值运算符函数,那么子类对象中的基类子对象将能够得到复制。如执行主程序中的如下语句:
Student student("张三", 20, 123456, "江苏南京市");
Student student1("李四", 25, 654321, "广东广州市");
student1 = student;
student.show();
student1.show();
则可以看到类Student的对象student1中的基类子对象内容变成了对象student中的基类子对象内容,姓名变成了“张三”,年龄变成了20,而对象student1中的扩展部分变成了对象student中的扩展部分,学号变成了123456,家庭住址变成了“江苏南京市”。
15.3 完整代码
本案例的完整代码如下所示:
#include
class Human
{
private:
char* m_name;
int m_age;
public:
Human (const char* name = “”, const int& age = 0) : m_age(age)
{
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
Human (Human const& human) : m_age(human.m_age)
{
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
}
Human& operator= (Human const& human)
{
if (&human != this)
{
delete [] m_name;
m_name = new char[strlen(human.m_name) + 1];
strcpy(m_name, human.m_name);
m_age = human.m_age;
}
return this;
}
void setName (const char name)
{
delete [] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void setAge (const int& age)
{
m_age = age;
}
std::string const name (void) const
{
return m_name;
}
const int& age (void) const
{
return m_age;
}
void eat (std::string const& food) const
{
std::cout << m_name << “正在吃” << food << std::endl;
}
void sleep (int hours)
{
std::cout << m_name << “睡了” << hours << “小时” << std::endl;
}
void show (void) const
{
std::cout << “” << m_name << “,年龄:” << m_age << std::endl;
}
};
class Student : public Human
{
private:
int m_no;
char* m_address;
public:
Student (const char* name, const int& age, const int& no, const char* address) : Human(name, age), m_no(no)
{
m_address = new char[strlen(address) + 1];
strcpy(m_address, address);
}
Student (Student const& student) : Human(student), m_no(student.m_no)
{
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
}
Student& operator= (Student const& student)
{
if (&student != this)
{
Human::operator=(student);
delete [] m_address;
m_address = new char[strlen(student.m_address) + 1];
strcpy(m_address, student.m_address);
m_no = student.m_no;
}
return *this;
}
void setNo (const int& no)
{
m_no = no;
}
const int& no (void) const
{
return m_no;
}
void learn (std::string const& course) const
{
std::cout << name () << “学” << course << std::endl;
}
void show (void) const
{
Human::show();
std::cout << “学号:” << m_no << “,住址:” << m_address << std::endl;
}
};
int main(int argc, const char * argv[])
{
Student student(“张三”, 20, 123456, “江苏南京市”);
Student student1(“李四”, 25, 654321, “广东广州市”);
student1 = student;
student.show();
student1.show();
return 0;
}