C++中的CONST
- const是伪关键字,不会改变什么
- const是常量
例1
#include <iostream>
#include <string>
int main()
{
const int MAX_AGE = 50;
int* a = new int;
*a = 2;
std::cout << *a << std::endl;
a = (int* )&MAX_AGE;
std::cout << *a << std::endl;
std::cin.get();
}
- 打印:
2
50 - 可以做两件事:
- 改变指针的内容,即指针指向的内存的内容
- 改变指针指向的地址
const int* a = new int
与int const * a = new int
效果一样`:意味着不能修改指针指向的内容;*a = 2会报错; 改变a本身则不会报错,即地址不是常量int* const a = new int
: 可以改变指针指向的内容,但是不能把实际的指针本身重新赋值const int* const a = new int
: 指针本身不能被赋值,指针指向的内容也不能被改变- 理解const: const的理解、const指针、指向const的指针
const的含义
例2
class Entity
{
private:
int m_X, m_Y;
public:
int GetX() const
{
return m_X;
}
};
- const在方法名之后,且只能在类中使用
- 这个方法不会修改任何实际的类,不能修改成员变量
- 如果在GetX()函数添加m_X = 2,会报错
- 只能读取,不能改写
class Entity
{
private:
int* m_X, m_Y;
public:
const int* const GetX() const
{
return m_X;
}
void SetX(int x)
{
m_X = x;
}
};
- 三个const
- 意思是GetX函数返回了一个不能被修改的指针,以及指向的内容也不能修改的指针
并且这个函数承诺不修改实际的Entity类 int* m_X, m_Y;
: 这样只是让m_X成为整型指针,如果想让所有变量是指针,要在每个变量前加*
为什么要声明函数是常量
例3
class Entity
{
private:
int m_X, m_Y;
public:
int GetX() const
{
return m_X;
}
void SetX(int x)
{
m_X = x;
}
};
void PrintEntity(const Entity& e)
{
e = Entity();
std::cout << e.GetX() << std::endl;
}
- 用常量引用来传递参数
- 参考
const Entity* e
的效果,可以e = nullptr
改变指针e本身的地址,但不能*e修改e指针指向的内容 - 那么
const Entity& e
的效果类似,不能修改e的赋值e = Entity()
, 因为引用就是内容,所以就是不能修改内容本身
class Entity
{
private:
int m_X, m_Y;
public:
int GetX()
{
m_X = 2;
return m_X;
}
void SetX(int x)
{
m_X = x;
}
};
void PrintEntity(const Entity& e)
{
std::cout << e.GetX() << std::endl;
}
- 跟上面相比,去掉了GetX函数的const,此时e.GetX()不能用
- 因为GetX函数已经不能保证它不会碰到(写入)Entity了
- 这是因为GetX函数是可以修改Entity类的,比如在函数中加m_X = 2;,但是PrintEntity的传入参数已经规定const Entity& e,即不能改变类本身内容
int GetX() const { return m_X; } int GetX() { return m_X; }
: 所以一般有两种版本的GetX函数的写法- 所以,如果函数实际上没有修改类或者不应该修改类的时候,应该总是标记方法为const;否则,在有常量引用的情况下,就用不了方法
- 但是有时候又需要修改一下变量:比如只是为了调试,不会真正影响程序;可以用mutable,意味着变量是可以改变的;允许函数是常量的情况,还可以修改变量
class Entity
{
private:
int m_X, m_Y;
mutable int var;
public:
int GetX() const
{
var = 2;
return m_X;
}
int GetX()
{
return m_X;
}
void SetX(int x)
{
m_X = x;
}
};
C++中的mutable关键字
两种用法:
- 与const一起;
-
- 在lambda中使用
与const使用
例4
class Entity
{
private:
std::string m_Name;
public:
const std::string& GetName() const
{
return m_Name;
}
};
int main()
{
const Entity e;
e.GetName();
std::cin.get();
}
- 不允许修改实际的类成员,返回的字符串是不能修改,即必须返回字符串常量
- 让方法GetName为const的原因是,在定义一个常量类的时候,即const Entity e,e是可以调用其方法GetName,因为GetName已经承诺不会改变实际的类了
- 在某种情况,需要碰到某个极特殊的变量,比如在调试的时候,需要传入一个Debug计数,这个时候可以使用mutable
class Entity
{
private:
std::string m_Name;
mutable int m_DebugCount = 0;
public:
const std::string& GetName() const
return m_Name;
}
};
在lambda使用
例5
int x = 8;
auto f = []()
{
std::cout << "Hello" << std::endl;
};
f();
- 假设我们想打印x,而不是“Hello”
这样是可以的:
auto f = [&x]()
{
std::cout << x << std::endl;
};
这样也是可以的:
auto f = [&]()
{
std::cout << x << std::endl;
};
这样也是可以的:
auto f = [x]()
{
std::cout << x << std::endl;
};
这样也是可以的:
auto f = [=]()
{
std::cout << x << std::endl;
};
但是,这样不行;因为x++做的操作实际上是创建一个新的变量y, 赋值给y,然后修改x:
auto f = [=]()
{
x++;
std::cout << x << std::endl;
};
跟这个有点像:
int y = x;
y++;
std::cout << y << std::endl;
- 令lambda是mutable
这样是可以的:
int x = 8;
auto f = [=]() mutable
{
x++;
std::cout << x << std::endl;
};
f();
通过值传递的变量,比如x,是可以被改变的
打印:8
为什么不是9呢?
因为是通过=,即值来传递参数进lambda,而不是引用;所以在x++时,创建了y,并将x+1赋值给y,而原来的x其实还没动过,所以x打印出来还是8。
C++的成员初始化列表
- 构造函数初始化列表,在构造函数中初始化类成员(变量)的一种方式
- 当编写一个类并向该类添加成员时,通常需要用某种方式对这些成员(变量)进行初始化,通常是在构造函数中
两种方法
例6
#include <iostream>
#include <string>
class Entity
{
private:
std::string m_Name;
public:
Entity() // 默认构造函数
{
m_Name = "Unknown";
}
Entity(const std::string& name)
{
m_Name = name;
}
const std::string& GetName() const { return m_Name; }
};
int main()
{
Entity e0;
std::cout << e0.GetName() << std::endl;
Entity e1("Cherno");
std::cout << e1.GetName() << std::endl;
std::cin.get();
}
打印:
Unknown
Cherno
#include <iostream>
#include <string>
class Entity
{
private:
std::string m_Name;
public:
Entity()
: m_Name("Unknown")
{
}
Entity(const std::string& name)
{
m_Name = name;
}
const std::string& GetName() const { return m_Name; }
};
·
int main()
{
Entity e0;
std::cout << e0.GetName() << std::endl;
Entity e1("Cherno");
std::cout << e1.GetName() << std::endl;
std::cin.get();
}
- 在构造函数和参数之后,可以添加一个冒号,通常喜欢写在下一行,然后列出想要初始化的成员,然后给它值
- 如果有两个变量,可以直接加逗号;注意:如果要这样初始化成员列表,一定要按顺序写这些变量
class Entity
{
private:
std::string m_Name;
int m_Score;
public:
Entity()
: m_Name("Unknown"), m_Score(0)
{
}
- 对于有参数的构造函数,也是一样的这样进行初始化:
Entity(const std::string& name)
:m_Name(name)
{
}
为什么要用成员初始化列表
- 因为在构造函数里如果一行一行编写初始化,然后后面还会接其他的操作,会显得比较凌乱
- 这样编写代码看着更加整齐
class Entity
{
private:
std::string m_Name;
int m_Score;
int x, y, z;
public:
Entity()
: m_Name("Unknown"), m_Score(0), x(0), y(0), z(0)
{
}
- 这样会使得m_Name构造两次,一个是使用默认构造函数,一个使用初始化的构造函数:
class Entity
{
private:
std::string m_Name;
int m_Score;
int x, y, z;
public:
Entity()
: m_Score(0), x(0), y(0), z(0)
{
m_Name = "Unknown";
}
实际上发生这样:m_Name = std::string("Unknown")
两次初始化构造函数
例 7
#include <iostream>
#include <string>
class Example
{
public:
Example()
{
std::cout << "Created Entity!" << std::endl;
}
Example(int x)
{
std::cout << "Created Entity with " << x << "!" << std::endl;
}
};
class Entity
{
private:
std::string m_Name;
Example m_Example;
public:
Entity()
{
m_Name = std::string("Unknown");
m_Example = Example(8);
}
Entity(const std::string& name)
:m_Name(name)
{
}
const std::string& GetName() const { return m_Name; }
};
int main()
{
Entity e0;
std::cin.get();
}
打印:
Created Entity!
Created Entity with 8!
- 一个是在private: Example m_Example产生的, 相当于这样:一次无参Example, 一次有参Example,所以会初始化两次
public:
Entity()
{
Example m_Example;
m_Name = std::string("Unknown");
m_Example = Example(8);
}
- 如果改成这样,只创建了一个实例:
public:
Entity()
: m_Example(Example(8))
{
m_Name = std::string("Unknown");
}
打印:
Created Entity with 8!
- 甚至可以写成:
public:
Entity()
: m_Example(8)
{
m_Name = std::string("Unknown");
}
例8
#include <iostream>
#include <string>
class Example
{
public:
Example()
{
std::cout << "Created Entity!" << std::endl;
}
Example(int x)
{
std::cout << "Created Entity with " << x << "!" << std::endl;
}
};
class Entity
{
private:
std::string m_Name;
Example m_Example;
public:
Entity()
{
m_Name = std::string("Unknown");
m_Example = Example(8);
}
Entity(const std::string& name)
:m_Name(name)
{
}
const std::string& GetName() const { return m_Name; }
};
int main()
{
Entity e0;
std::cout << e0.GetName() << std::endl;
Entity e1("Cherno");
std::cout << e1.GetName() << std::endl;
std::cin.get();
}
打印:
Created Entity!
Created Entity with 8!
Unknown
Created Entity!
Cherno
- 如果改成这样:
public:
Entity()
: m_Example(Example(8))
{
m_Name = std::string("Unknown");
}
打印:
Created Entity with 8!
Unknown
Created Entity!
Cherno
应该到处使用成员初始化列表