C++学习笔记 (01)
目录
1. C++ 虚函数和纯虚函数的区别
1.1 C++ 虚函数:
1.1.1 定义虚函数和纯虚函数目的
* 定义虚函数是为了允许用基类的指针来调用子类的相同名称函数
* 定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数
1.1.2 "虚"函数的虚是指什么
* 虚函数的虚, 虚在所谓"推迟联编"或者"动态联编"上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。
* 由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为"虚"函数。
* 虚函数只能借助于指针或者引用来达到多态的效果
1.1.3 示例
代码示例:类的继承,无虚函数
#include <iostream>
class Entity {
public:
std::string GetName() {
return "Entity";
}
};
class Player : public Entity {
private:
std::string mPlayerName;
public:
Player(std::string name) : mPlayerName(name) {}
std::string GetName() { return mPlayerName; }
};
void PrintName(Entity *e) {
std::cout << "Entity name: " << e->GetName() << std::endl;
}
int main() {
Entity *e = new Entity();
PrintName(e);
std::cout << "-----------new Entity-------------" << std::endl;
Player *p = new Player("John");
PrintName(p);
std::cout << "------------new Player------------" << std::endl;
Entity *p2 = p;
PrintName(p2);
}
执行结果:
代码示例:虚函数实现多态
#include <iostream>
class Entity {
public:
virtual std::string GetName() {
return "Entity";
}
};
class Player : public Entity {
private:
std::string mPlayerName;
public:
Player(std::string name) : mPlayerName(name) {}
std::string GetName() override { return mPlayerName; }
};
void PrintName(Entity *e) {
std::cout << "Entity name: " << e->GetName() << std::endl;
}
int main() {
Entity *e = new Entity();
PrintName(e);
std::cout << "-----------new Entity-------------" << std::endl;
Player *p = new Player("John");
PrintName(p);
std::cout << "------------new Player------------" << std::endl;
Entity *p2 = p;
PrintName(p2);
}
执行结果:
1.2. C++纯虚函数:
1.2.1 纯虚函数是什么
* 纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但强制要求任何派生类都要实现自己的方法。在基类中实现纯虚函数的方法是在函数原型后加 =0:
1.2.2 引入原因
* 为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
* 在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
1.2.3 解决方法
* 为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。
1.2.4 纯虚函数特点
* 声明了纯虚函数的类是一个抽象类。用户不能创建类的实例,只能创建它的派生类的实例。
* 纯虚函数最显著的特征是:必须在继承类中重新声明函数(不要后面的=0,否则该派生类也不能实例化),而且在抽象类中往往没有定义。
* 定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。
* 纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的默认实现。
代码示例:派生类正常实现纯虚函数的接口
#include <iostream>
class Printable {
public:
virtual std::string GetClassName() = 0; // Pure virtual function
};
class Entity : public Printable {
public:
virtual ~Entity() {}
virtual std::string GetName() { return "Entity"; }
std::string GetClassName() override { return "Entity"; } // Override to provide class name
};
class Player : public Entity {
private:
std::string mPlayerName;
public:
Player(std::string name) : mPlayerName(name) {}
std::string GetName() override { return mPlayerName; }
};
void PrintName(Entity *e) {
std::cout << "Entity name: " << e->GetName() << std::endl;
}
void Print(Printable *p) {
std::cout << "Class name: " << p->GetClassName() << std::endl;
}
int main()
{
Entity *e = new Entity();
// PrintName(e);
Player *p = new Player("John");
// PrintName(p);
std::cout << "----------------------------------" << std::endl;
Print(e);
Print(p);
std::cout << "----------------------------------" << std::endl;
delete p;
delete e;
}
执行结果:
1.2.5 特别说明
class Entity里面实现了一个虚析构函数,如果没有这个虚析构函数会报如下错误
原因是C++的继承中,父类指针指向子类的时候,即发生动态联编的情况下,析构时只调用了父类的析构函数,没有调用子类的析构函数,会造成内存泄漏。
父类的析构函数使用虚函数就能避免此类问题。
1.2.6 示例
代码示例:派生类实现纯虚函数的接口,接口为虚函数
#include <iostream>
class Printable {
public:
virtual std::string GetClassName() = 0; // Pure virtual function
};
class Entity : public Printable {
public:
virtual ~Entity() {}
virtual std::string GetName() { return "Entity"; }
virtual std::string GetClassName() override { return "Entity"; } // Override to provide class name
};
class Player : public Entity {
private:
std::string mPlayerName;
public:
Player(std::string name) : mPlayerName(name) {}
std::string GetName() override { return mPlayerName; }
std::string GetClassName() override { return "Player"; } // Override to provide class name
};
void Print(Printable *p) {
std::cout << "Class name: " << p->GetClassName() << std::endl;
}
class A : public Printable {
public:
std::string GetClassName() override { return "A"; }
};
int main()
{
Entity *e = new Entity();
Player *p = new Player("John");
std::cout << "----------------------------------" << std::endl;
Print(e);
Print(p);
std::cout << "----------------------------------" << std::endl;
A *a = new A();
Print(a);
delete p;
delete e;
}
执行结果:
2. C++中的CONST用法
2.1 const常量和变量中的CONST用法
const用法示例
#include <iostream>
#include <string>
void ConstantValue()
{
const int value = 10; // This is a constant value
value = 20; // This will cause a compilation error, as value is a constant, cannot be changed
const int *va = new int; // This is a pointer to a constant integer
*va = 30; // This will cause a compilation error, as *va is a constant, cannot be changed
va = &value; // This is allowed, as va is a pointer to a constant integer
int * const vb = new int; // This is a constant pointer to an integer
*vb = 40; // This is allowed, as *vb is not a constant
vb = &value; // This will cause a compilation error, as vb is a constant pointer, cannot be changed
const int * const vc = new int; // This is a constant pointer to a constant integer
*vc = 50; // This will cause a compilation error, as *vc is a constant, cannot be changed
vc = &value; // This will cause a compilation error, as vc is a constant pointer, cannot be changed
std::cout << "The constant value is: " << value << ", va is: " << *va << std::endl;
delete va; // Don't forget to delete the dynamically allocated memory
delete vb; // Don't forget to delete the dynamically allocated memory
}
int main()
{
ConstantValue();
return 0;
}
2.2 类中const用法
2.2.1 const类函数
类中函数后面加上const, 表示函数不可修改类的成员变量
const类函数
#include <iostream>
#include <string>
class Entity {
private:
int mX, mY;
public:
Entity(int x, int y) : mX(x), mY(y) {}
int GetX() const // const function, cannot modify member variables
{
// Error: cannot modify member variable in const function
// mX = 100;
return mX;
}
int SetX(int x)
{
mX = x;
}
virtual std::string GetName() { return "Entity"; }
};
void Print(Entity e) {
std::cout << "mx = " << e.GetX() << std::endl;
}
int main()
{
Print(Entity(1, 10));
return 0;
}
2.2.2 const类实例用法
const类实例只能调用类中申明为const的函数
const类实例
#include <iostream>
#include <string>
class Entity {
private:
int mX, mY;
public:
Entity(int x, int y) : mX(x), mY(y) {}
int GetX()
{
mX = 13;
return mX;
}
int GetX() const
{
return mX;
}
int SetX(int x)
{
mX = x;
}
virtual std::string GetName() { return "Entity"; }
};
void Print(const Entity &e) { // Pass by reference
std::cout << "mx = " << e.GetX() << std::endl;
}
void Print2(Entity &e) { // Pass by reference
std::cout << "mx = " << e.GetX() << std::endl;
}
int main()
{
Print(Entity(1, 10));
return 0;
}
3. C++中的mutable用法
3.1 mutable的常见用法
在const实例中调用const方法,可能需要进行一些调试,例如debug计数.
这种场景需要使用mutable,代表了90%场景.
mutable示例
#include <iostream>
#include <string>
class Entity {
private:
std::string m_Name;
mutable int m_DebugCount;
public:
Entity() : m_Name("Entity"), m_DebugCount(0) {}
const std::string &GetName() const
{
m_DebugCount++;
return m_Name;
}
};
int main()
{
const Entity e;
std::cout << e.GetName() << std::endl;
return 0;
}
3.2 lambda表达式中使用mutable
lambda表达式中的mutable
#include <iostream>
#include <string>
int main()
{
int x = 8;
// Lambda function that captures x by reference
auto f = [&x]()
{
x++;
std::cout << "in lambda f, x = " << x << std::endl;
};
f();
int y = 18;
// Lambda function that captures x by value
auto f2 = [=]() mutable
{
y++; // This will not affect the original x
std::cout << "in lambda f2, y = " << y << std::endl; // This will print the incremented x value 9
};
f2();
std::cout << "out of lambda f2, y = " << y << std::endl; // This will print the original x value 8
return 0;
}