一、构造函数的参数初始化列表
注意:1.初始化参数列表优先于当前对象的构造函数执行
2.子对象的初始化顺序和在其初始化列表的排序无关,但是和在类中的声明顺序有关,先声明的先初始化
#include <iostream>
using namespace std;
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;
}
};
//对象的初始化列表: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;
}
};
int main(int argc, char * argv[])
{
Student s1(001, 1997,1,1);
return 0;
}
二、静态成员变量和静态成员函数
(1) 静态成员变量
1)在C++中,我们可以使用静态成员变量来实现多个对象共享数据的目标,静态成员变量是一种特殊的成员变量,有关键词static修 饰
#include <iostream>
using namespace std;
class Student
{
public:
static int count; //静态成员变量,所有对象共享同一个静态成员变量
private:
int id;
public:
Student()
{
count++;
id = count;
}
static int GetCount() //静态成员函数
{
//id++; //静态成员函数只能调用静态成员变量
return count;
}
int GetCount1()
{
return count; //普通函数可以访问静态成员变量
}
};
int Student::count = 0; // 静态成员变量一定要在类的外部初始化
int main(int argc, char *argv[])
{
Student s1;
Student s2;
cout<<s1.count<<endl;
cout<<s2.count<<endl;
cout<<Student::count<<endl; //静态成员变量可以直接通过类名来访问
cout<<Student::GetCount()<<endl; //静态成员函数可以直接通过类名来访问
cout<<s1.GetCount1()<<endl;
return 0;
}
三、友元
3.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 = "<<m_a<<endl;
}
};
void Show(Test &t)
{
cout<<"m_a = "<<t.m_a<<endl;
}
int main(int argc, char * argv[])
{
Test t1;
t1.show();
Show(t1);
return 0;
return 0;
}
3.2 友元类
#include <iostream>
using namespace std;
class Test2;
class Test
{
friend class Test2; //友元类,可以在类Test2中访问Test的私有成员
private:
int m_a;
public:
Test()
{
m_a = 100;
}
void show()
{
cout<<"m_a = "<<m_a<<endl;
}
};
class Test2
{
friend class Test; //友元类
private:
int m_b;
public:
Test2()
{
m_b = 200;
}
void print(Test &t) //友谊是单向的
{
cout<<"m_a = "<<t.m_a<<endl;
}
};
int main(int argc, char *argv[])
{
Test t1;
Test2 t2;
t2.print(t1);
return 0;
}
四、继承和派生
4.1 继承的概念和语法
继承可以理解为一个类从另一个类获取成员变量和成员函数的过程
例如:类B继承于类A,那么B就用有A中的成员变量和成员函数。被继承的类称为父类或者基类,继承的类称为子类或者派生类。
4.2 继承的语法
#include <iostream>
#include <cstring>
using namespace std;
class Person //父类或者基类
{
protected:
char m_name[32];
int m_age;
public:
Person()
{
strcpy(m_name,"小苗");
m_age = 18;
}
};
class Student:public Person //子类或者派生类,public继承权限
{
private:
int id;
public:
Student(int id)
{
this->id;
}
void show()
{
cout<<"m_name = "<<m_name<<" m_age = "<<m_age<<" id = "<<id<<endl;
}
};
int main(int argc, char *argv[])
{
Student s1(100);
s1.show();
return 0;
}
4.3 继承的权限
(1)共有继承(public)
1.基类的public在派生类中具有public属性,派生类的内部和外部都可以访问
2.基类的protected在派生类中有protected属性,派生类的内部可以访问,外部不可以访问
3.基类中的private在派生类中不可以访问。
(2)保护继承(protected)
1.基类的public在派生类中具有protected属性,派生类的内部可以访问,外部不可以访问
2.基类的protected在派生类中有protected属性,派生类的内部可以访问,外部不可以访问
3.基类中的private在派生类中不可以访问。
(3)私有继承(private)
1.基类的public在派生类中具有private属性,派生类的内部可以访问,外部不可以访问
2.基类的protected在派生类中有private属性,派生类的内部可以访问,外部不可以访问
3.基类中的private在派生类中不可以访问。
#include <iostream>
using namespace std;
class TestA //父类或者基类
{
private:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class TestB:private TestA //子类继承
{
private:
int m_a; //不可继承
private:
int m_b; //在派生类中私有,只能在类中访问
private:
int m_c; //在派生类中私有,只能在类中访问
public:
void show()
{
m_a++; //在TestA中,m_a是私有成员变量,不能访问
m_b++;
m_c++;
}
};
class TestC:protected TestA //子类继承
{
private:
int m_a;
protected:
int m_b; //在派生类中为保护属性,可以在本类和继承的类中访问
private: //在派生类中为保护属性,可以在本类和继承的类中访问
int m_c;
public:
void show()
{
m_a++; //在TestA中,m_a是私有成员变量,不能访问
m_b++;
m_c++;
}
};
class TestD:public TestA //子类继承
{
private:
int m_a; //不可继承
protected: //在派生类中为保护属性,可以在本类和继承的类中访问
int m_b;
public:
int m_c; //在派生类中为公有属性,可以在类的内部和外部访问
public:
void show()
{
m_a++; //在TestA中,m_a是私有成员变量,不能访问
m_b++;
m_c++;
}
};
int main(int argc, char *argv[])
{
TestB t1;
t1.show();
TestC t2;
t2.show();
TestD t3;
t3.show();
return 0;
}
4.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;
}
4.5 继承的构造
类的构造函数不能被继承,因为即使继承了基类的构造函数,名字也和派生类不一样。
构造顺序:
先构造基类,在构造类成员对象,最后构造自己,构造顺序和形参初始化列表无关。
类的构造函数和析构函数不能被继承
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
protected:
char m_name[32];
int m_age;
public:
Person(const char *name, int age)
{
cout<<"person的构造函数"<<endl;
strcpy(this->m_name, name);
m_age = age;
}
~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:
int m_id;
Date birth;
public:
Student(int id):birth(1996,1,1),Person("xiaoming",25)
{
cout<<"student的构造函数"<<endl;
m_id = id;
}
void show()
{
cout<<" m_name = "<<m_name<<" m_age = "<<m_age<<" m_id = "<<m_id<<endl;
}
~Student()
{
cout<<"Student的析构函数"<<endl;
}
};
int main(int argc, char const *argv[])
{
Student s1(100);
s1.show();
return 0;
}
4.6 const修饰成员函数
1)const修饰成员函数
1.成员函数后加const称这个函数为常函数;
2.常函数不可以修改成员变量;
3.成员变量前加mutable后,即可在常函数中修改。
2)常对象
1.声明对象前加const的对象称为常对象;
2.常对象只能调用常函数。
#include <iostream>
using namespace std;
class Student
{
private:
int m_id;
char *m_name;
int m_age;
public:
Student(int id, char *name, int age);
void show();
char *GetName() const;
int GetId() const;
int GetAge() const;
};
void Student::show()
{
cout<<"m_name = "<<m_name<<" m_age = "<<m_age<<" m_id = "<<m_id<<endl;
}
char* Student::GetName() const //成员函数必须在声明和定义处都加上const关键词
{
return m_name;
}
int Student::GetId() const //成员函数必须在声明和定义处都加上const关键词
{
return m_id;
}
int Student::GetAge() const //成员函数必须在声明和定义处都加上const关键词
{
return m_age;
}
Student::Student(int id, char* name, int age):m_id(id),m_name(name),m_age(age)
{
}
int main(int argc, char const *argv[])
{
Student s1(100, "Xiaoming", 19);
s1.show();
return 0;
}
4.7 同名成员
#include <iostream>
using namespace std;
class TestA
{
private:
int m_a;
public:
void show()
{
cout<<"this is TestA"<<endl;
}
};
class TestB:public TestA
{
private:
int m_a;
public:
void show()
{
cout<<"this is TestB"<<endl;
}
};
int main(int argc, char const *argv[])
{
TestB tb;
cout<<sizeof(tb)<<endl;
tb.show(); //同名,默认调用派生类中的成员函数,基类成员被隐藏
tb.TestA::show(); //显示调用
return 0;
}
4.8 继承中的static关键词
如果在基类中定义了静态成员变量,则静态成员变量将被所有多有的派生类共享
#include <iostream>
using namespace std;
class Person
{
public:
static int count;
Person()
{
this->count++;
}
};
int Person::count = 0;
class Student:public Person
{
};
int main(int argc, char const *argv[])
{
Student s1;
Student s2;
Person p1;
Person p2;
//Person p3;
cout<<Person::count<<endl; //派生类和基类共享一个静态成员变量
cout<<Student::count<<endl;
return 0;
}
4.9 继承中的类型兼容性原则
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
protected:
char m_name[32];
int m_age;
public:
Person()
{
cout<<"Person的无参构造函数"<<endl;
strcpy(m_name,"LDD");
m_age = 18;
}
void SetValue(char *name, int age)
{
strcpy(m_name,name);
m_age = age;
}
Person(const Person &p)
{
cout<<"Parent的拷贝构造"<<endl;
strcpy(m_name,p.m_name);
m_age = p.m_age;
}
void print()
{
cout<<"m_name = "<<m_name<<" m_age = "<<m_age<<endl;
}
};
class Student:public Person
{
private:
int id;
public:
void SetId(int i)
{
id = i;
}
void show()
{
cout<<"m_name = "<<m_name<<" m_age = "<<m_age<<" id = "<<id<<endl;
}
};
int main(int argc, char const *argv[])
{
Person p1;
p1.SetValue("zhangsan",18);
p1.print();
Student s1;
s1.SetValue("lisi",20);
s1.SetId(200);
Student *ps = &s1;
ps->show();
Person *p = &s1; //基类指针可以直接指向派生类对象 子类指针不能指向派生类对象
p->SetValue("wnger",30);
p->print();
Person &p2 = s1; //基类引用可以直接指向派生类对象
p2.print();
Person p3 = s1; //子类对象可以直接初始化基类对象
p3.print();
return 0;
}
4.10 多继承
1.多继承概念
1)派生类中只有一个基类,被称为单继承,除此之外,C++也支持多继承,即一个派生类可以有多个基类
2)多继承容易让代码逻辑复杂,思维混乱,一直备受争议,在中小型项目中,很少使用。
2.多继承的语法
#include <iostream>
using namespace std;
class BaseA
{
protected:
int m_a;
int m_b;
public:
BaseA(int a, int b);
~BaseA();
};
BaseA::BaseA(int a, int b):m_a(a),m_b(b)
{
cout<<"BaseA构造函数被调用"<<endl;
}
BaseA::~BaseA()
{
cout<<"BaseA析构函数被调用"<<endl;
}
class BaseB
{
protected:
int m_c;
int m_d;
public:
BaseB(int c, int d);
~BaseB();
};
BaseB::BaseB(int c, int d):m_c(c),m_d(d)
{
cout<<"BaseB构造函数被调用"<<endl;
}
BaseB::~BaseB()
{
cout<<"BaseB析构函数被调用"<<endl;
}
class Deived:public BaseA,public BaseB
{
private:
int m_e;
public:
Deived(int a, int b, int c, int d, int e);
~Deived();
void show()
{
cout<<m_a<<" , "<<m_b<<" , "<< m_c<<" , "<<m_d<<" , "<<m_e<<endl;
}
};
Deived::Deived(int a, int b, int c, int d, int e):BaseA(a,b),BaseB(c,d),m_e(e)
{
cout<<"deived的构造函数"<<endl;
}
Deived::~Deived()
{
cout<<"deived的析构函数"<<endl;
}
int main(int argc, char const *argv[])
{
Deived d(1,2,3,4,5);
d.show();
return 0;
}
3.多继承的二义性
#include <iostream>
using namespace std;
class BaseA
{
protected:
int m_a;
};
BaseA::BaseA()
{
cout<<"BaseA构造函数被调用"<<endl;
}
BaseA::~BaseA()
{
cout<<"BaseA析构函数被调用"<<endl;
}
class BaseB:public BaseA
{
protected:
int m_b;
int m_c;
int m_d;
};
BaseB::BaseB()
{
cout<<"BaseB构造函数被调用"<<endl;
}
BaseB::~BaseB()
{
cout<<"BaseB析构函数被调用"<<endl;
}
class BaseC:public BaseA
{
protected:
int m_c;
};
BaseC::BaseC()
{
cout<<"BaseA构造函数被调用"<<endl;
}
BaseC::~BaseC()
{
cout<<"BaseA析构函数被调用"<<endl;
}
class BaseD:public BaseB,public BaseC
{
protected:
int m_d;
};
BaseD::BaseD()
{
cout<<"BaseA构造函数被调用"<<endl;
}
BaseD::~BaseD()
{
cout<<"BaseA析构函数被调用"<<endl;
}
int main(int argc, char const *argv[])
{
BaseD td;
cout<<sizeof(td)<<endl;
td.m_a; //报错
return 0;
}
5.虚继承
#include <iostream>
using namespace std;
class BaseA
{
protected:
int m_a;
};
class BaseB:virtual public BaseA
{
protected:
int m_b;
};
class BaseC:virtual public BaseA
{
protected:
int m_c;
};
class BaseD:public BaseB,public BaseC
{
protected:
int m_d;
};
int main(int argc, char const *argv[])
{
BaseB tb;
cout<<sizeof(tb)<<endl;
cout<<&tb<<endl;
cout<<&tb.m_a<<endl; //报错
cout<<&tb.m_b<<endl; //报错
cout<<"************************"<<endl;
BaseD td;
cout<<sizeof(td)<<endl;
cout<<&td<<endl;
cout<<&td.m_a<<endl; //报错
cout<<&td.m_b<<endl; //报错
cout<<&td.m_c<<endl; //报错
cout<<&td.m_d<<endl; //报错
return 0;
}
6.向上转型
#include <iostream>
using namespace std;
class Parent
{
private:
int m_a;
};
class Child:public Parent
{
private:
int m_b;
};
int main(int argc, char const *argv[])
{
int a = 11.7;
double b = 2;
Parent p;
Child c;
p = c; //只能将派生类对象赋值给基类对象,派生类引用,指针赋值给基类引用, 指针->向上转型
//c = p;
return 0;
}