《C++文章汇总》
上一篇介绍了引用和汇编《07-内存管理、命名空间和继承》,本文初始化列表、父类构造函数、虚函数和多态。
1.初始化列表:和构造函数的汇编代码一样
◼特点
一种便捷的初始化成员变量的方式
只能用在构造函数中
初始化顺序只跟成员变量的声明顺序有关
struct Person {
int m_age;
int m_height;
// Person(int age,int height){
// m_age = age;
// m_height = height;
// }
Person(int age,int height):m_age(age),m_height(height){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
18
180
初始化列表可以接收表达式和函数
int func(){return 8;}
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_age(func()),m_height(height+2){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
8
182
赋值顺序变化,初始化只与成员变量的定义顺序有关
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_age(m_height),m_height(height+2){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
32766
182
在构造函数中改变成员变量的赋值顺序,结果并不会发生改变
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_height(height+2),m_age(m_height){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
32766
182
如果改变m_age和m_height的定义顺序,结果会改变
struct Person {
int m_height;
int m_age;
Person(int age,int height):m_height(height+2),m_age(m_height){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
182
182
通过函数调用能看到顺序
int myAge(){
cout << "myAge()" << endl;
return 10;
};
int myHeight(){
cout << "myHeight()" << endl;
return 10;
};
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_height(myHeight()),m_age(myAge()){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
myAge()
myHeight()
10
10
2.初始化列表与默认参数配合使用:相当于写了三个构造函数
struct Person {
int m_age;
int m_height;
Person(int age = 1,int height = 125):m_age(age),m_height(height){
};
};
int main(){
Person person;
Person person0();//函数声明
Person person1(17);
Person person2(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
cout << person1.m_age << endl;
cout << person1.m_height << endl;
cout << person2.m_age << endl;
cout << person2.m_height << endl;
return 0;
}
//输出
1
125
17
125
18
180
初始化列表函数中的赋值,会在自动生成的赋值语句后面执行
struct Person {
int m_age;
int m_height;
Person(int age = 1,int height = 125):m_age(age),m_height(height){
m_age = 10;
};
};
int main(){
Person person;
Person person0();//函数声明
Person person1(17);
Person person2(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
cout << person1.m_age << endl;
cout << person1.m_height << endl;
cout << person2.m_age << endl;
cout << person2.m_height << endl;
return 0;
}
//输出
10
125
10
125
10
180
◼ 如果函数声明和实现是分离的
初始化列表只能写在函数的实现中
默认参数只能写在函数的声明中
struct Person {
int m_age;
int m_height;
Person(int age = 1,int height = 125);
};
Person::Person(int age,int height):m_age(age),m_height(height){
m_age = 10;
};
int main(){
Person person;
Person person0();//函数声明
Person person1(17);
Person person2(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
cout << person1.m_age << endl;
cout << person1.m_height << endl;
cout << person2.m_age << endl;
cout << person2.m_height << endl;
return 0;
}
//输出
10
125
10
125
10
180
3.构造函数的互相调用
构造函数之间的互相调用在初始化列表中,在构造函数Person()中会将外面的person对象传入到Person(10,20)中,Person(10,20)中改变的m_age,m_height的值就为外面的Person对象的成员变量的值
struct Person {
int m_age;
int m_height;
Person():Person(10,20){
//创建了一个临时的person对象
Person(10,20);
// Person person;
// person.m_age = 10;
// person.m_height = 20;
}
Person(int age,int height){
m_age = age;
m_height = height;
}
};
int main(){
Person person;
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
10
20
◼ 注意:下面的写法是错误的,初始化的是一个临时对象,传入的Person(int age,int height)方法中对象的地址是临时对象的地址,传入汇编,并不会改变外面person对象的成员变量m_age和m_height的值
struct Person {
int m_age;
int m_height;
Person(){
//创建了一个临时的person对象
Person(10,20);
// Person person;
// person.m_age = 10;
// person.m_height = 20;
}
Person(int age,int height){
m_age = age;
m_height = height;
}
};
int main(){
Person person;
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//输出
-272632816
32766
4.父类的构造函数
◼ 子类的构造函数默认会调用父类的无参构造函数
struct Person{
int m_age;
Person(){
cout << "Person::Person()" << endl;
}
};
struct Student:Person{
int m_no;
Student(){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//输出
Person::Person()
Student::Student()
◼ 如果子类的构造函数显式地调用了父类的有参构造函数,就不会再去默认调用父类的无参构造函数
struct Person{
int m_age;
Person(){
cout << "Person::Person()" << endl;
}
Person(int age){
cout << "Person::Person(int age)" << endl;
}
};
struct Student:Person{
int m_no;
Student():Person(10){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//输出
Person::Person(int age)
Student::Student()
◼ 如果父类缺少无参构造函数,子类的构造函数必须显式调用父类的有参构造函数
struct Person{
int m_age;
Person(int age){
cout << "Person::Person(int age)" << endl;
}
};
struct Student:Person{
int m_no;
Student():Person(10){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//输出
Person::Person(int age)
Student::Student()
◼ 父类没有构造函数,就不调用父类构造函数了
struct Person{
int m_age;
};
struct Student:Person{
int m_no;
Student(){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//输出
Student::Student()
◼ 父类子类成员变量同时初始化
class Person{
int m_age;
public:
Person(int age) :m_age(age){}
};
class Student:Person{
int m_no;
public:
Student(int no,int age) :m_no(no),Person(age){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student(10,20);
return 0;
}
5.构造、析构顺序
构造顺序:先父类后子类
析构顺序:先子类后父类
struct Person {
Person(){
cout << "Person::Person()" << endl;
};
~Person(){
cout << "Person::~Person()" << endl;
};
};
struct Student:Person{
Student(){
cout << "Student::Student()" << endl;
};
~Student(){
cout << "Student::~Student()" << endl;
};
};
int main(){
{
Student student;
}
return 0;
}
//输出
Person::Person()
Student::Student()
Student::~Student()
Person::~Person()
6.多态
◼ 父类指针可以指向子类对象,是安全的,开发中经常用到(继承方式必须是public)
struct Person {
int m_age;
};
struct Student:Person{
int m_score;
};
int main(){
Person *p = new Student();
p->m_age = 10;//安全区域
getchar();
return 0;
}
◼ 子类指针指向父类对象是不安全的:编译不会报错,编译器只管是什么类型,运行时会报错与否,与编译器的容错能力有关,Xcode会报错error: 'm_score' is a private member of 'Student
struct Person {
int m_age;
};
struct Student:Person{
int m_score;
};
int main(){
// Person *p = new Student();
// p->m_age = 10;//安全区域
Student *p = (Student *)new Person();
p->m_age = 10;
p->m_score = 99;//不安全,实质上p指针指向的地址Person对象里面并不存在m_score的区域,p->m_score指向的是未知区域,可能误将别人的数据抹掉或篡改掉
getchar();
return 0;
}
若是class类,则继承方式默认为private,父类的指针就不能指向子类的对象了,此时在父类前要加上public
class Person {
public:
int m_age;
};
class Student: public Person{
int m_score;
};
int main(){
Person *p = new Student();
p->m_age = 10;//安全区域
getchar();
return 0;
}
◼ 默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态
◼ 多态是面向对象非常重要的一个特性
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
在运行时,可以识别出真正的对象类型,调用对应子类中的函数
struct Animal{
void speak(){
cout << "Animal::speak()" << endl;
}
void run(){
cout << "Animal::run()" << endl;
}
};
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
void run(){
cout << "Dog::run()" << endl;
}
};
struct Cat:Animal{
void speak(){
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
};
struct Pig:Animal{
void speak(){
cout << "Pig::speak()" << endl;
}
void run(){
cout << "Pig::run()" << endl;
}
};
void liu(Animal *p){
p->speak();
p->run();
}
int main(){
liu(new Dog());
liu(new Cat());
liu(new Pig());
Cat *p = (Cat *)new Dog();
p->speak();//call Cat::speak
p->run();//call Cat::run
return 0;
}
//输出
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
Cat::speak()
Cat::run()
查看汇编发现,在调用函数时会直接在函数面前加上指针类型,指针类型为Animal *,故全部打印为Animal,那么C++如何实现多态呢?
◼ 多态的要素
子类重写父类的成员函数(override)
父类指针指向子类对象
利用父类指针调用重写的成员函数
7.虚函数
◼ C++中的多态通过虚函数(virtual function)来实现
◼ 虚函数:被virtual修饰的成员函数
◼ 只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(也就是说子类中可以省略virtual关键字)
struct Animal{
virtual void speak(){
cout << "Animal::speak()" << endl;
}
virtual void run(){
cout << "Animal::run()" << endl;
}
};
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
void run(){
cout << "Dog::run()" << endl;
}
};
struct Cat:Animal{
void speak(){
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
};
struct Pig:Animal{
void speak(){
cout << "Pig::speak()" << endl;
}
void run(){
cout << "Pig::run()" << endl;
}
};
void liu(Animal *p){
p->speak();
p->run();
}
int main(){
liu(new Dog());
liu(new Cat());
liu(new Pig());
Cat *p = (Cat *)new Dog();
p->speak();//call Cat::speak
p->run();//call Cat::run
return 0;
}
//输出
Dog::speak()
Dog::run()
Cat::speak()
Cat::run()
Pig::speak()
Pig::run()
Dog::speak()
Dog::run()