1.1 结构体中的静态
上一节的笔记中的static的情形适用于变量和函数。
通过类是实例来引用静态变量是没有意义的,就像是类的全局实例,静态方法也是一样,同样无法访问类的实例。
静态方法可以被调用,但是不可以引用到类的实例。 静态方法没有类是实例。
使用struct通常默认情况下是public
1: 一般为了可以访问输出静态成员变量,需要在类外部定义它们的实际存储空间,即在全局作用域中定义静态变量,给它预留内存。 2:静态函数只可访问静态成员变量。
#include <iostream>
struct Entity
{
static int x,y;
static void Print()
{
std::cout << x << " , " << y << std::endl;
}
};
int Entity::x;
int Entity::y;
int main()
{
Entity e;
Entity::x = 2;
Entity::y = 3;
e.Print();
std::cin.get();
}
结果:
1.2 引用与指针实现静态函数访问非静态变量
如果想要实现静态函数访问非静态成员变量,可以通过传入一个对象的引用或指针作为参数来实现静态函数访问非静态变量。
关于C++中的引用 对象的引用是一种别名,允许使用一个已经存在的对象来创建新的名称以便通过新名称来访问对象。引用是在声明时初始化的,之后不能再引用其他对象。
#include <iostream>
struct Entity
{
int x,y;
static void Print(Entity& entity)
{
std::cout << entity.x << " " << entity.y << std::endl;
}
};
int main()
{
Entity e;
e.x = 2;
e.y = 3;
Entity::Print(e);
std::cin.get();
}
结果:(对象的引用来实现访问非静态成员变量,其实还是在类中完成的)
注意:静态变量还是静态方法,对于赋值都是 Entity::x / Entity::Print(e);
#include <iostream>
class MyClass{
public:
static int staticVariable;
static void staticMethod()
{
std::cout << "static method called." << std::endl;
}
};
int MyClass::staticVariable = 42;//初始化静态成员变量
int main()
{
MyClass* ptr = nullptr;
ptr->staticMethod();//通过指针调用静态方法
std::cout << ptr->staticVariable << std::endl;
//通过指针访问静态成员变量
MyClass::staticMethod();//直接通过类名调用静态方法
std::cout << MyClass::staticVariable << std::endl;
//直接通过类名访问静态成员变量
return 0;
}
结果:
MyClass* ptr = nullptr;
声明了一个指向MyClass
对象的指针,并将其初始化为nullptr
。这意味着该指针当前不指向任何有效的对象。当使用一个空指针(nullptr
)来访问对象或调用对象的成员时,会导致未定义行为。(**在使用空指针访问非静态成员变量或调用非静态方法时,会导致未定义行为或者段错误。)**因为空指针并不指向任何有效的内存位置,因此无法从中访问对象的成员。
? 为什么上面的代码可以正常运行,不是不指向任何有效的内存位置吗?
#include <iostream>
class MyClass {
public:
int variable;
void method() {
std::cout << "Method called." << std::endl;
}
};
int main() {
MyClass* ptr = nullptr; // 将指针初始化为nullptr
ptr->variable = 42; // 使用空指针访问对象的成员变量,导致未定义行为
ptr->method(); // 使用空指针调用对象的方法,导致未定义行为
return 0;
}
结果:
分析:
"Segmentation fault"指的是一种常见的程序运行错误,它通常发生在访问了无效的内存地址或者访问了不被允许访问的内存区域时。这个错误在程序运行时会导致程序崩溃,并且在命令行下会输出一个类似于"Segmentation fault (core dumped)"的错误信息。"Segmentation fault"的产生通常有以下几种原因:访问了空指针或者已经释放的指针:这是最常见的产生"Segmentation fault"的原因之一。使用空指针或者已经释放的指针访问非法的内存地址时,就会产生该错误。访问了未初始化的变量或数组越界:如果访问未初始化的变量或者数组越界,就会访问到不被允许访问的内存区域,从而导致"Segmentation fault"。栈内存溢出:当使用递归函数或者分配大量的局部变量时,可能会导致栈内存溢出,也就是说申请的栈内存超出了系统限制,从而导致"Segmentation fault"。动态内存分配错误:在使用动态内存分配函数(例如malloc、calloc、realloc)分配内存时,如果分配的大小不正确或者释放动态内存的顺序不正确,则可能导致"Segmentation fault"。
!!:虽然使用了空指针nullptr来访问静态成员变量和调用静态方法,但是由于静态成员 和静态方法属于类本身而不是类的实例,它们在程序启动时就已经被初始化,并且 它们的内存位置是固定的。 因此,即使通过空指针访问静态成员变量或调用静态方法,在编译期间仍然可以解析和执行这些操作,因为静态成员和静态方法不依赖于特定的对象实例。 在这段代码中,语句ptr->staticMethod()实际上是通过指针访问类的静态方法。尽管ptr是一个空指针,但在调用静态方法时,不需要实际的对象实例,因此不会导致运行时错误。 同样,访问静态成员变量ptr->staticVariable实际上是通过指针访问类的静态成员变量。由于静态成员变量的内存分配是在编译期间完成的,因此不需要特定的对象实例来访问静态成员变量。 最后,通过类名直接调用静态方法MyClass::staticMethod()和直接访问静态成员变量MyClass::staticVariable也是正确的,因为它们不依赖于对象实例。 需要注意的是,在使用空指针访问非静态成员变量或调用非静态方法时,会导致未定义行为或者段错误。因此,在实际开发中,仍然应该谨慎使用空指针,并确保在访问成员或调用方法之前对指针进行有效性检查。
!!!!空指针也是一种指针类型,在访问静态成员变量和调用静态方法时,由于静态成员变量和静态方法不属于特定对象实例,而是属于整个类,在程序启动时就已经被初始化,并且其内存地址是固定的。因此,无论是通过对象指针还是空指针来访问静态成员变量和静态方法,编译器都会根据静态成员和静态方法的地址进行访问,而不是依赖于特定的对象实例。因此,使用空指针来访问静态成员变量和静态方法是合法的。 需要注意的是,在访问非静态成员变量和非静态方法时,需要使用有效的对象指针,否则会导致未定义行为或者段错误。因此,在实际编程中,应该避免使用空指针来访问非静态成员变量和非静态方法。
可以使用对象指针来访问非静态成员与方法,其实可以直接 对象.成员变量(成员方法) 访问来
int main() {
MyClass obj;
MyClass* ptr = &obj; // 将指针ptr指向有效的MyClass对象
ptr->staticMethod(); // 通过指针调用静态方法
std::cout << ptr->staticVariable << std::endl; // 通过指针访问静态成员变量
return 0;
}
1.3 局部静态(local static)
其实就是static 后其他人不可以轻易修改;
单例模式:单例模式是一种设计模式,它的主要目的是确保一个类只有一个实例,并提供全局访问点。 单例模式的应用场景如下:
1 资源共享:某些对象在系统中只需要存在一个实例,例如数据库连接池、线程池等。通过使用单例模式,可以确保只创建一个实例并提供给其他组件进行访问,避免资源的浪费和冲突。
2 配置信息:某些配置信息只需要在系统初始化时读取一次,并在运行过程中被多个组件共享。使用单例模式可以确保配置信息只被读取一次,并且其他组件可以通过单例实例获取到最新的配置。
3 日志记录器:日志记录器在系统中需要被频繁使用,如果每次记录日志都创建一个新的实例,会对性能产生负面影响。通过单例模式,可以共享同一个日志记录器实例,简化日志记录操作,并且统一管理日志记录逻辑。
4 对象缓存:在某些场景下,需要缓存一些对象以提高系统性能。通过使用单例模式,可以确保只有一个缓存实例存在,并且提供统一的接口进行对象的添加、获取、删除等操作。
5 控制对象的数量:某些情况下,系统中需要控制某个类型的对象只能有限个数存在,使用单例模式可以方便地管理这些对象的创建和销毁。
总之,单例模式的主要作用是提供一个全局唯一的实例,方便对该实例进行统一的管理和调用。它可以在多个组件之间共享资源、配置信息、状态等,并且能够简化系统中对实例的访问和管理。
#include <iostream>
class Singleton{
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
//禁止拷贝构造函数和赋值操作符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
//私有构造函数,确保只能通过getInstance获取实例
Singleton() {}
//私有析构函数,防止外部删除实例
~Singleton() {}
};
int main()
{
Singleton& singleton1 = Singleton::getInstance();
Singleton& singleton2 = Singleton::getInstance();
//使用singleton1进行操作,并输出单例实例地址
std::cout << "Address of singleton1: " << &singleton1 << std::endl;
std::cout << "Address of singleton2: " << &singleton2 << std::endl;
// 将singleton1赋值给singleton3
Singleton& singleton3 = singleton1;
// 输出singleton3的地址,与singleton1和singleton2应该相同
std::cout << "Address of singleton3: " << &singleton3 << std::endl;
return 0;
}
结果:
主要就是 方法内部的
代码演示了单例模式的实现。在这个类中,有一个静态方法 getInstance(),它返回一个对单例实例的引用。实例是在该方法内部作为静态变量创建的,确保只实例化一次。 Singleton 类还将拷贝构造函数和赋值操作符声明为删除,防止通过复制或赋值创建额外的实例。 在 main() 函数中,使用 Singleton::getInstance() 获取单例实例。两个变量 singleton1 和 singleton2 被赋值为该实例,并打印它们的地址以确认它们指向同一个对象。 此外,将 singleton1 赋值给 singleton3,并打印 singleton3 的地址以验证它与 singleton1 和 singleton2 是相同的。 总体而言,这段代码通过确保整个程序中只能存在一个类的实例来实现单例模式。
基础:成员变量是描述对象状态的属性,成员函数是描述对象行为的方法,而局部变量是 在函数执行期间临时创建的变量。
#include <iostream>
using namespace std;
class Circle{
private:
double radious;//私有成员变量
public://构造函数初始化成员变量
Circle(double r){
radious = r;
}
double getArea(){//共有成员函数
double area = 3.14 * radious *radious;//局部变量area
return area;
}
void disPlayInfo()
{
cout << "圆半径:" << radious << endl;
cout << "圆面积:" << getArea() << endl;
}
};
int main(){
Circle c(5);
c.disPlayInfo();
return 0;
}
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
解析:static Singleton instance; 是单例模式的一种实现方式。这行代码用于声明一个静态的 Singleton 类型的成员变量 instance,它将作为单例实例在类中被共享。
static
静态成员变量:
- 在类中声明的静态成员变量属于类本身,而不是类的实例。它被所有类的实例共享,只有一份拷贝。
- 静态成员变量在程序运行期间只被初始化一次,在类的任何实例中的修改都会对其他实例可见。 可
- 以通过类名加作用域运算符 :: 直接访问静态成员变量,也可以通过对象访问(不推荐)。
- 通常在需要在类的所有实例之间共享数据时使用静态成员变量。
静态成员函数:
- 静态成员函数不依赖于类的实例,可以直接通过类名调用。
- 静态成员函数不能访问非静态成员变量,因为非静态成员变量属于对象实例。
- 静态成员函数可以访问静态成员变量和其他静态成员函数。
静态局部变量:
- 在函数内部声明的静态局部变量在程序运行期间只被初始化一次,且仅在第一次进入函数时被分配内存。
- 静态局部变量在函数调用之间保持存在,并且具有静态存储期。
- 静态局部变量对于函数来说是“全局”的,即使在函数执行完成后仍然保留其值,下次再次进入函数时可以继续使用。