c++ 虚函数 & 纯虚函数 & 可见性 &字符字面量 & const关键字

2023-03-04

虚函数

虚函数是一种c++的机制,它允许一个类的子类能够重写(override)父类的函数,实现它自己独特的功能。
下面是一个例子

#include <iostream>
#include <string>

class Entity
{
public:
	virtual std::string GetName() {
		return "Entity";
	}
	Entity() {

	};
	~Entity() {

	};
private:
};

class Player : public Entity {
private:
	std::string m_name;
public:
	Player(const std::string& name) {
		m_name = name;
	}
	std::string GetName() override{
		return m_name;
	}

};

int main() {
	Entity* e = new Entity;
	std::cout << e->GetName() << std::endl;
	Player* p = new Player("wsxk");
	std::cout << p->GetName() << std::endl;
	Entity* e2 = new Player("test");
	std::cout << e2->GetName() << std::endl;

}

可以看出,虚函数还有一个作用就是使得 父类指针指向子类对象时(总是成立,因为子类是父类的超集,所以父类有的方法,子类也都有)可以正确的使用子类方法。

纯虚函数

纯虚函数只要声明在这个类中,这个类就无法实例化对象,且强制要求该类子类必须实现纯虚函数的定义。

#include <iostream>
#include <string>
class Printable {
public:
	virtual std::string GetClassName() = 0;
};

class Entity: public Printable
{
public:
	virtual std::string GetName(){
		return "Entity";
	}
	Entity() {

	};
	~Entity() {

	};
	std::string GetClassName() override{
		return "Entity";
	}
private:
};

class Player : public Entity {
private:
	std::string m_name;
public:
	Player(const std::string& name) {
		m_name = name;
	}
	std::string GetName() override{
		return m_name;
	}
	std::string GetClassName() override{
		return "Player";
	}
};

void PrintClassName(Printable * obj) {
	std::cout << obj->GetClassName() << std::endl;
}

int main() {
	Entity* e = new Entity;
	PrintClassName(e);

	//std::cout << e->GetName() << std::endl;
	Player* p = new Player("wsxk");
	PrintClassName(p);
	//std::cout << p->GetName() << std::endl;
	//Entity* e2 = new Player("test");
	//std::cout << e2->GetName() << std::endl;
}

可见性

可见性其实指的是private,protected,public
在类中使用的三种可见性对应如下功能:

  1. private 指类外不能访问,其子类也不能访问,类内的方法可以访问
  2. protected 指类外不能访问,其子类的方法可以访问,类内的方法可以访问
  3. public 指类外能访问,子类能访问,类内的方法可以访问

protected其实涉及继承问题。

#include <iostream>

class Entity {
public:
	int x;
	void set(int a, int b, int c) {
		x = a;
		y = b;
		z = c;
	}
protected:
	int y;
private:
	int z;
};

class child : public Entity {
public:
	void set(int a, int b, int c) {
		x = a;
		y = b;
		//z = c;//不可访问
	}
};

int main() {
	Entity e;
	e.set(1, 2, 3);
	child c;
	c.x = 2;
	c.set(1, 2, 3);
}

在这里面,在child继承Entity后,child同样拥有Entity中的变量y和z,但是x消失了(child类中并没有这个变量)

字符字面量

出现形如

const char * name = "12345";

的东西,"12345"就说一个字符字面量,它会被存储在用户只读的访问区域内,因此形如

char * name = "12345";
name[2] = 'a';

是无效的,未定义的行为。

番外

顺道一提,其实c++的字符串中的每个字符可以是1,2,4个字节,如下图所示

const char * name = u8"wsxk";//utf-8 1 byte
const char16_t * name1 = u"wsxk";//utf-16 2bytes
const char32_t * name2 = U"wsxk";//utf-32 4bytes
const wchar_t * name3 = L"wsxk";//由编译器认定,windows通常是2bytes,linux是4bytes

另外,可以用一个神秘字符,来增加代码可读性:

const char * name = R"(line1
line2
line3
line4)";

//等价
const char * name = "line1\n"
"line2\n"
"line3\n"
"linr4\n";

const

const只是一个语法上的承诺,你承诺这个变量是个常量,仅此而已
const int *a 表示a指向的地址中的内容不能被修改,可以理解为我把指向内容的int类型声明为const不准修改了,因为const和int在一起
int * const a 表示a指向的地址不能修改,可以理解为这个const和指针名在一起,表示a存储的值(指向的地址)不能改变。

const用在class中的function

#include <iostream>

class Entity {
public:
	int x, y;
	int getX() const {
		return x;
	}
};
int main() {
	Entity e;
}

这种说明意味着 Entity类中的getX方法承诺不会修改类中的属性

但是如果你在调试的时候想用一下应该怎么办呢?
于是乎出现了mutable关键字。

#include <iostream>

class Entity {
public:
	int x, y;
	mutable int m_Debugcount=0;
	int getX() const {
		m_Debugcount++;
		return x;
	}
};
int main() {
	Entity e;
	
}

这种用法是可行的。