前言
此文为小甲鱼大佬的《C++快速入门》第十六讲内容笔记整理。
this指针
在“对象”的世界里,有一个特殊的指针,它叫做this。从下面这个典型的栗子来认识它。
class Human{
char fishc;
Human(char fishc);//构造函数,里面的参数定义为fishc
}
Human::Human(char fishc){ //构造函数初始化
fishc = fishc;
}
此程序的意图是想将传入参数赋值给属性的fishc,但是因为它们的名字一样,这样子的话构造器就有可能认不出来。因为它不知道你是要将属性去覆盖参数还是讲传入的参数去覆盖属性。
如何构造器知道哪个是参数、哪个又是属性呢?
这时候就需要用到它了——this指针。
this -> fishc = fishc
上句的含义是:这个对象的fishc的属性 = 传入的参数。
注意:
使用this指针的基本原则是:如果代码不存在二义性隐患,就不必使用this指针!
this指针会在更加高级的方法里用到(这个未完待续,等我学到后面再扩充这部分)
类的继承
继承机制使得程序员可以创建一个类的堆叠层次结构(金字塔形),每个子类均集成在它的基类(父类或者超类)里定义的方法和属性。
用途:通过继承机制,程序员可以对现有的代码进行进一步的扩充,并应用在新的程序中。
#include <iostream>
#include <string>
class Animal
{
public:
std::string mouth;
void eat();
void sleep();
void drool();
};
class Pig : public Animal
{
public:
void climb();
};
class Turtle : public Animal
{
public:
void swim();
};
void Animal::eat()
{
std::cout << "I am eating!" << std::endl;
}
void Animal::sleep()
{
std::cout << "I am sleeping!" << std::endl;
}
void Animal::drool()
{
std::cout << "I am drooling!" << std::endl;
}
void Pig::climb()
{
std::cout << "I am climbing!" << std::endl;
}
void Turtle::swim()
{
std::cout << "I am swimming!" << std::endl;
}
int main()
{
Pig pig;
Turtle turtle;
pig.eat();
turtle.eat();
pig.climb();
turtle.swim();
return 0;
}
I am eating!
I am eating!
I am climbing!
I am swimming!
可以看到,子类Pig 和 Turtle 中并未定义相关的方法,但因为其继承于父类Animal,而父类中拥有这些方法,所以可以使用。
再论this指针
回顾下:this指针是类的一个自动生成、自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象的地址。
当一个对象被创建时,该对象的this指针就自动指向对象数据的首地址。
先看一个简单的栗子:
#include<iostream>
class Point
{
private:
int x, y;
public:
Point(int a, int b)
{
x = a;
y = b;
}
void MovePoint(int a, int b)
{
x = a;
y = b;
}
void print()
{
std::cout << "x=" << x <<"\n"<< "y=" << y << std::endl;
}
};
int main()
{
Point point1(10, 10);
point1.MovePoint(2, 2);
point1.print();
return 0;
}
x=2
y=2
- 当对象point1调用MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。
- MovePoint函数的原型事实上应该是 void MovePoint( Point *this, int a, int b);
- 第一个参数是指向该类对象的一个指针,我们在定义成员函数时没看见是因为这个参数在类中是隐含的。
- 这样point1的地址传递给了this,所以在MovePoint函数中便可以显式的写成:void MovePoint(int a, int b) { this->x = a; this->y = b;}
- 即可以知道,point1调用该函数后,也就是point1的数据成员被调用并更新了值。
接下来验证一下:当一个对象被创建时,该对象的this指针就自动指向对象数据的首地址。将this指针的地址和对象的地址打印出来。
#include <iostream>
#include <string>
class Pet
{
public:
Pet(std::string theName);
~Pet();
static int getCount();
protected:
std::string name;
private:
static int count;
};
class Dog : public Pet
{
public:
Dog(std::string theName);
};
class Cat : public Pet
{
public:
Cat(std::string theName);
};
int Pet::count = 0; // 注意这一句:他起码做了两件事
Pet::Pet(std::string theName)
{
name = theName;
count++;
std::cout << "一只宠物出生了,名字叫做: " << name << "\n";
}
Pet::~Pet()
{
count--;
std::cout << name << "挂掉了\n";
}
int Pet::getCount()
{
return count;
}
Dog::Dog(std::string theName) : Pet(theName)
{
std::cout << "this:" << this << "\n";
}
Cat::Cat(std::string theName) : Pet(theName)
{
}
int main()
{
Dog dog("Tom");
std::cout << "dog:" << &dog << "\n";
Cat cat("Jerry");
std::cout << "\n已经诞生了" << Pet::getCount() << "只宠物!\n\n";
Dog dog_2("Tom_2");
std::cout << "dog:" << &dog_2 << "\n";
Cat cat_2("Jerry_2");
std::cout << "\n现在呢,已经诞生了" << Pet::getCount() << "只宠物!\n\n";
std::cout << "\n现在还剩下 " << Pet::getCount() << " 只宠物!\n\n";
return 0;
}
一只宠物出生了,名字叫做: Tom
this:0x6ffdf0
dog:0x6ffdf0
一只宠物出生了,名字叫做: Jerry已经诞生了2只宠物!
一只宠物出生了,名字叫做: Tom_2
this:0x6ffdd0
dog:0x6ffdd0
一只宠物出生了,名字叫做: Jerry_2现在呢,已经诞生了4只宠物!
现在还剩下 4 只宠物!Jerry_2挂掉了
Tom_2挂掉了
Jerry挂掉了
Tom挂掉了
可以看到地址是一样的。
在任何一个方法里都可以使用this指针。从本质上讲,C++中的对象其实是一种特殊的结构--除了变量,还包含一些函数的特殊结构。
在程序运行时,对象的属性(变量)和方法(函数)都是保存在内存里,这就意味着它们各自都有与之相关联的地址。
这些地址都可以通过指针来访问,而this指针毋庸置疑是保存着对象本身的地址。
每当我们调用一个方法的时候,this指针都会随着你提供的输入参数被秘密地传递个那个方法。
正是因为如此,我们才能够在方法里像使用一个局部变量那样使用this指针。
又因为静态变量不是属于某个特定的对象,(一个对象生成之后是放在栈里面的)而是由全体对象共享的,(每个对象都有this指针)这就意味着它们(静态变量)无法访问this指针。所以,我们才无法在静态方法里访问非静态的类成员(因为非静态的类成员需要用到隐含的this指针来访问)。
总结
此讲简单介绍了在C++中的this指针和继承的概念,随着自己理解的深入,我也将逐渐丰富这些笔记。
参考视频
《C++快速入门--小甲鱼》https://www.bilibili.com/video/av28127959?from=search&seid=13080921018912796262