一 构造函数
1.1 构造函数的重载和调用
和普通成员一样,构造函数是允许重载的,一个类可以由多个重载的构造函数,在创建对象的时候,根据传递实参的不同来决定调用哪个构造函数。
#include <iostream> using namespace std; class Array { private: int size; //数组的容量 int *data; //数组首地址 public: Array(); //无参构造函数 Array(int s); //有参构造函数 Array(int s,int z); //有两个参数的构造函数 void setVal(int Index,int value); int GetVal(int Index); ~Array(); }; Array::Array() { cout<<"Array的无参构造函数"<<endl; size = 5; data = (int *)malloc(sizeof(int)*size); } Array::Array(int s) //有参构造函数 { size = s; cout<<"Array的有一个参数的构造函数,size ="<<size<<endl; data = (int *)malloc(sizeof(int *)*size); } Array::Array(int s,int z) //有两个参数的构造函数 { size = s; cout<<"Array的有两个参数的构造函数"<<endl; data = (int *)malloc(sizeof(int *)*size); } void Array::setVal(int Index,int value) { data[Index] = value; } int Array::GetVal(int Index) { return data[Index]; } Array::~Array() { cout<<"Array的析构函数"<<endl; if(data != NULL) { free(data); data = NULL; } } int main(int argc, char const *argv[]) { Array a1; //创建对象,自动调用构造函数 for(int i = 0 ; i < 5;i++) { a1.setVal(i,i+1); } for(int i = 0; i < 5;i++) { cout<<a1.GetVal(i)<<" "; } cout<<endl; //Array a2(10); //括号法 //Array a3(10,20); //Array a4 = 100; //等号法 Array a5 = (10,20); return 0; }
注意:使用等号法时要注意,只能调用单个参数的构造函数,
1.2 拷贝构造函数
<1> 概念
class Name(const className &obj),拷贝构造函数也称为赋值构造函数 作用:用一个类去初始化另一个类对象
<2> 拷贝构造函数的调用时机
1> 用一个对象去初始化另一个对象
//Array a6(a2); Array a7 = a1;
2> 当函数形参是一个对象的时候
void print(Array a) { //a.GetVal(0); } print(a1);
3> 函数的返回值是一个对象
#include <iostream> using namespace std; class Array { private: int size; //数组的容量 int *data; //数组首地址 public: Array(); //无参构造函数 Array(int s); //有参构造函数 Array(int s,int z); //有两个参数的构造函数 Array(const Array &obj); void setVal(int Index,int value); int GetVal(int Index); ~Array(); }; Array::Array() { cout<<"Array的无参构造函数"<<endl; size = 5; data = (int *)malloc(sizeof(int)*size); } Array::Array(int s) //有参构造函数 { size = s; cout<<"Array的有一个参数的构造函数,size ="<<size<<endl; data = (int *)malloc(sizeof(int *)*size); } Array::Array(int s,int z) //有两个参数的构造函数 { size = s; cout<<"Array的有两个参数的构造函数"<<endl; data = (int *)malloc(sizeof(int *)*size); } Array::Array(const Array &obj) { //size = obj.size; //data = obj.data; cout<<"Array的拷贝构造函数"<<endl; } void Array::setVal(int Index,int value) { data[Index] = value; } int Array::GetVal(int Index) { return data[Index]; } Array::~Array() { cout<<"Array的析构函数"<<endl; if(data != NULL) { //free(data); data = NULL; } } void print(Array a) { //a.GetVal(0); } Array& Func() { Array a1; return a1; } int main(int argc, char const *argv[]) { Array a1; //创建对象,自动调用构造函数 for(int i = 0 ; i < 5;i++) { a1.setVal(i,i+1); } for(int i = 0; i < 5;i++) { cout<<a1.GetVal(i)<<" "; } cout<<endl; //Array a2(10); //括号法 //Array a3(10,20); //Array a4 = 100; //等号法 //Array a5 = (10,20); //Array a6(a2); //Array a7 = a1; //print(a1); Array a8 = Func(); //Array(); cout<<"***************"<<endl; return 0; }
<3> 深拷贝
#include <iostream> using namespace std; class Array { private: int size; //数组的容量 int *data; //数组首地址 public: Array(); //无参构造函数 Array(int s); //有参构造函数 Array(int s,int z); //有两个参数的构造函数 Array(const Array &obj); void setVal(int Index,int value); int GetVal(int Index); ~Array(); }; Array::Array() { cout<<"Array的无参构造函数"<<endl; size = 5; data = (int *)malloc(sizeof(int)*size); } Array::Array(int s) //有参构造函数 { size = s; cout<<"Array的有一个参数的构造函数,size ="<<size<<endl; data = (int *)malloc(sizeof(int *)*size); } Array::Array(const Array &obj) //深拷贝 { cout<<"Array的拷贝构造函数"<<endl; size = obj.size; data = (int *)malloc(sizeof(int)*size); for(int i = 0 ;i < size;i++) { data[i] = obj.data[i]; } } void Array::setVal(int Index,int value) { data[Index] = value; } int Array::GetVal(int Index) { return data[Index]; } Array::~Array() { cout<<"Array的析构函数"<<endl; if(data != NULL) { free(data); data = NULL; } } int main(int argc, char const *argv[]) { Array a1(10); Array a2(a1); Array a3 = a1; return 0; }
<4> 默认构造函数
#include <iostream> using namespace std; /* 1.一旦提供了无参构造函数,系统不会再提供默认的无参构造函数 2.一旦提供了有参构造函数后,系统将不会再提供默认的无参构造函数 */ class Demo { public: /*Demo() { cout<<"Demo的无参构造函数"<<endl; }*/ /*Demo(const Demo &obj) { cout<<"Demo的拷贝构造函数"<<endl; }*/ Demo(int x) { cout<<"Demo的拷贝构造函数"<<endl; } }; int main(int argc, char const *argv[]) { Demo d1(1); //会调用系统提供的无参构造函数 Demo d2 = d1;//会调用系统提供的拷贝构造函数 //Demo d3;//一旦提供了无参构造函数,系统不会再提供默认的无参构造函数 return 0; }
1.3 析构函数
格式:~类名(); //析构函数:函数名是类名加~,没有返回值
1.3.1 概念
类创建对象时,系统会自动调用构造函数进行初始化工作,同样,销毁对象时,系统会调用析构函数进行资源回收和清理工作,如释放内存,释放句柄,关闭文件等 《注意》:析构函数没有参数,不能被重载,一个类只有一个析构函数,编译器也会自动生成一个默认的析构函数
1.3.2 对象的动态创建和释放
#include <iostream> using namespace std; class Test { public: Test() { cout<<"Test的构造函数"<<endl; } ~Test() { cout<<"Test的析构函数"<<endl; } }; int main(int argc, char const *argv[]) { //Test t1; //栈空间创建对象 /*Test *pt = (Test *)malloc(sizeof(Test)*1); if(NULL == pt) { cout<<"malloc failure"<<endl; } free(pt);*/ Test *pt2 = new Test; //1.申请内存(堆),2自动调用构造函数 delete pt2; //自动调用析构函数 return 0; }
1.3.3 构造函数的参数初始化列表
#include <iostream> using namespace std; class Date { private: int m_year; int m_month; int m_day; public: /*Date() { cout<<"data的构造函数"<<endl; m_year = 1937; m_month = 7; m_day = 7; }*/ Date(int y,int m,int d) { cout<<"Date有参构造函数"<<endl; m_year = y; m_month = m; m_day = d; } }; //对象的初始化列表:1.类对象作为成员变量并且该类没有提供无参构造函数,2.成员变量用const修饰 class Student { private: const int id; Date birth; public: Student(int i,int y,int m,int d):birth(y,m,d),id(i) { cout<<"student的有参构造函数"<<endl; //id = i; } }; int main(int argc, char const *argv[]) { Student s1(1000,1999,1,1); return 0; }
几点说明: 1>初始化列表优选于当前对象的构造函数执行 2> 子对象的构造顺序和其在类中声明的顺序有关,和参数初始化列表初始化的顺序无关 3> 构造函数和析构函数调用的顺序正好相反
1.4 静态成员变量和静态成员函数
(1)静态成员变量
1>在c++中我们可以使用静态成员变量来实现多个对象共享数据的目标,静态成员变量是一种特殊的变量,有static关键词修饰。
#include <iostream> using namespace std; class Student { public: static int count; //静态成员变量,所有对象共享同一个静态成员变量 private: int id; public: Student() { count++; id = count; //count = 1; } static int GetCount() //静态成员函数 { //id++; //静态成员函数只能访问静态成员变量 return count; } void func() { count++; //普通函数可以访问静态成员变量 } }; //静态成员变量一定要在类的外部初始化 int Student::count = 0; int main(int argc, char const *argv[]) { Student s1; Student s2; cout<<s1.count<<endl; cout<<s2.count<<endl; cout<<Student::count<<endl; //静态成员变量可以直接通过类名来访问 cout<<Student::GetCount()<<endl;//静态成员函数可以直接通过类名来访问 cout<<s1.GetCount()<<endl; return 0; }
1.5 友元
概念: 一个类中私有成员变量只能在类的内部访问,不能在类的外部访问,现在,有这么一种机制,友元,借助友元,我们可以在类的外部去访问类内的私有成员。
1.5.1 友元函数
#include <iostream> using namespace std; class Test { friend void show(Test &t); private: int m_a; public: Test() { m_a =100; } void show() { cout<<m_a<<endl; } }; void show(Test &t) { cout<<t.m_a<<endl; } int main(int argc, char const *argv[]) { Test t1; show(t1); return 0; }
1.5.2 友元类
#include <iostream> using namespace std; class A { friend class B; //友谊具有单向性 private: int m_a; public: A() { m_a = 100; } void print(B &b) { cout<<b.m_b<<endl; } }; class B { public: void print(A &a) { cout<<a.m_a<<endl; } private: int m_b; }; int main(int argc, char const *argv[]) { A a1; B b1; b1.print(a1); return 0; }
二 继承和派生
2.1 概念
继承可以理解为一个类从另一个类中获取成员变量和成员方法的过程,例如类B继承于类A,那么B就拥有A中所有的成员变量和方法,被继承的类称为父类或者基类,继承的类称为子类或者派生类
2.2 继承的语法
#include <iostream> #include <cstring> using namespace std; class Person //父类或者基类 { //private: protected: char m_name[32]; int m_age; public: Person() { strcpy(m_name,"张三"); m_age = 18; } }; class Student:public Person //子类或者派生类 { private: int m_id; public: Student(int id) { m_id = id; } void show() { cout<<"m_name = "<<m_name<<" m_age = "<<m_age<<" m_id = "<<m_id<<endl; } }; int main(int argc, char const *argv[]) { Student s1(1000); s1.show(); return 0; }
2.3 继承的权限
<1> 公有继承:基类中的所有属性原来具有什么样的权限,在派生类中保持不变,基类中的私有属性在派生类中不能访问。 <2> 保护继承:基类中的public在派生类中具有保护属性,其它不变,基类中的私有属性在派生类中不能访问。 <3> 私有继承:基类中public和protected都变成私有属性,基类中的私有属性在派生类中不能访问。
#include <iostream> using namespace std; class TestA { private: int m_a; protected: int m_b; public: int m_c; }; class TestB:private TestA //私有继承 { /* private: //不可访问 int m_a; private: int m_b; private: int m_c; */ public: void test() { //m_a++; this->m_b++; this->m_c++; } }; class TestC:protected TestA //保护继承 { /* private: int m_a; //不可访问 protected: int m_b; protected: int m_c; */ public: void test() { //m_a++; this->m_b++; this->m_c++; } }; class TestD:public TestA //公有继承 { /* private: //不可访问 int m_a; protected: int m_b; public: int m_c; */ public: void test() { //m_a++; this->m_b++; this->m_c++; } }; int main(int argc, char const *argv[]) { TestB b1; //b1.m_c = 1; TestD d1; d1.m_c = 1; return 0; }
2.4 继承模型
#include <iostream> using namespace std; class Person { public: int m_age; }; class Student:public Person { public: int m_id; }; int main(int argc, char const *argv[]) { Student s1; cout<<sizeof(s1)<<endl; cout<<&s1<<endl; cout<<&s1.m_age<<endl; cout<<&s1.m_id<<endl; return 0; }
2.5 继承中的构造
类的构造函数不能被继承,因为即使继承了,基类构造函数的名字和派生类也不一样,不能成为派生类的构造函数 为了初始化基类成员,需要在派生类的构造函数的初始化列表里面显示调用基类的构造函数。
#include <iostream> #include <cstring> using namespace std; class Person { protected: char name[32]; int age; public: Person(const char *n,int a) { cout<<"Person的有参构造函数"<<endl; strcpy(name,n); age = a; } ~Person() { cout<<"Person的析构函数"<<endl; } }; class Date { private: int m_year; int m_month; int m_day; public: Date(int y,int m,int d) { cout<<"Date有参构造函数"<<endl; m_year = y; m_month = m; m_day = d; } ~Date() { cout<<"Date的析构函数"<<endl; } }; class Student:public Person { private: Date birth; int id; public: //当基类没有提供无参构造函数的时候,派生类需要通过对象初始化列表来传参 Student(int i):Person("aaa",23),birth(1997,1,1) { cout<<"Student的构造函数"<<endl; id = i; } void show() { cout<<name<<" "<<age<<" "<<id<<endl; } ~Student() { cout<<"Student的析构函数"<<endl; } }; int main(int argc, char const *argv[]) { Student s1(1000); s1.show(); return 0; }
2.6 const修饰成员函数
#include <iostream> using namespace std; class Student { private: char *m_name; int m_age; float m_score; public: Student(char *n,int a,float s); void show(); char *GetName() const; int GetAge() const; float GetScore() const; }; Student::Student(char *n,int a,float s):m_name(n),m_age(a),m_score(s) { } void Student::show() { cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl; } char* Student::GetName() const //成员函数必须在声明和定义处都加上const { return m_name; } int Student::GetAge() const { //m_age++; //常成员函数修饰只读成员变量 return m_age; } float Student::GetScore() const { return m_score; } int main(int argc, char const *argv[]) { Student s1("zhangsan",12,98); s1.show(); return 0; }
2.7 同名成员
基类和派生类中出现同名函数(原型相同)时,默认调用派生类中的成员,基类中的成员函数会被隐藏
#include <iostream> using namespace std; class TestA { private: int a; public: void show() { cout<<"this is TestA"<<endl; } }; class TestB:public TestA { private: int b; public: void show() { cout<<"this is TestB"<<endl; } }; int main(int argc, char const *argv[]) { TestB b1; b1.show(); //同名,默认调用派生类成员函数 b1.TestA::show(); return 0; }
2.8 继承中的static关键词
如果在基类中定义了静态成员变量,则该静态成员变量将被所有的派生类共享
#include <iostream> using namespace std; class Person { public: static int count; public: Person() { count++; } }; int Person::count = 0; class Student:public Person { }; int main(int argc, char const *argv[]) { Student s1; Student s2; Student s3; Student s4; Student s5; Person p1; Person p2; cout<<Person::count<<endl; cout<<Student::count<<endl; return 0; }