C++编程法则 静态成员函数和非静态成员函数之间的相互调用


Chapter1 C++静态成员函数和非静态成员函数之间的相互调用

一直对C++静态成员函数和非静态成员函数之间的相互调用记不住,都是死记硬背,今天突然醍醐灌顶;

1、静态成员函数不能调用非静态成员函数,因为静态成员函数属于类,没有this指针,而普通成员函数的调用需要this指针,所以,静态成员函数不能调用非静态成员函数;

2、普通成员函数可以调用静态成员函数,因为静态成员函数放在静态区,整个类和类对象都可以访问。

需要注意的是:静态数据成员不能在类中初始化,一般在类外和main()函数之前初始化,缺省时初始化为0。

1.static成员的所有者是类本身,但是多个对象拥有一样的静态成员。从而在定义对象是不能通过构造函数对其进行初始化。

2.静态成员不能在类定义里边初始化,只能在class body外初始化。
3.静态成员仍然遵循public,private,protected访问准则。
4.静态成员函数没有this指针,它不能返回非静态成员,因为除了对象会调用它外,类本身也可以调用

静态成员属于全局变量,是所有实例化以后的对象所共享的,而成员的初始化你可以想象成向系统申请内存存储数据的过程,显然这种共有对象。不能在任何函数和局部作用域中初始化。

class point{
public:
	point(){};
//	...
private:
	static int x,y;
};

// 类外初始化,不必再加static关键字 
int point::x = 0;
int point::y = 0;
 
int main(){
//	...
}

为什么?

因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。

什么东西能在类内初始化
能在类中初始化的成员只有一种,那就是静态常量成员。

这样不行

class A
{ private: static int count = 0; // 静态成员不能在类内初始化 };

这样也不行

class A
{ private: const int count = 0; // 常量成员也不能在类内初始化 };

但是这样可以

class A
{ private: static const int count = 0; // 静态常量成员可以在类内初始化 };

结论:

  • 静态常量数据成员可以在类内初始化(即类内声明的同时初始化),也可以在类外,即类的实现文件中初始化,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化;
  • 静态非常量数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
  • 非静态的常量数据成员不能在类内初始化,也不能在构造函数中初始化,而只能且必须在构造函数的初始化列表中初始化;
  • 非静态的非常量数据成员不能在类内初始化,可以在构造函数中初始化,也可以在构造函数的初始化列表中初始化;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

为什么static数据成员一定要在类外初始化

这是因为被static声明的类静态数据成员,其实体远在main()函数开始之前就已经在全局数据段中诞生了(见《Inside The C++ Object Model》page247)!其生命期和类对象是异步的,(而且静态语意说明即使没有类实体的存在,其静态数据成员的实体也是存的)这个时候对象的生命期还没有开始,如果你要到类中去初始化类静态数据成员,让静态数据成员的初始化依赖于类的实体,,那怎么满足前述静态语意呢?难道类永远不被实例化,我们就永远不能访问到被初始化的静态数据成员吗

静态成员变量隶属于类,不是某个对象,所以静态成员变量不可能占用某一个对象的存储空间,所以静态成员需要再类外部定义,以便静态成员变量在全局数据区分配其存储空间。

Chapter2 类的静态成员函数调用类非静态成员的方法(传入类指针)

原文链接:https://blog.csdn.net/u010810750/article/details/81873408

在类中使用静态成员函数是一种破坏封装的行为,因为静态成员函数只能调用类的静态成员。但是在有些情况下只能使用静态成员函数,比如类内绑定自身成员函数作为回调函数,这种情况在开启多线程时很常见,如果不想将回调定义为全局那只能定义为类静态了,为了避免过度破坏封装类中应当尽量不要让类静态成原函数调用类成员。这种情况下可以用一种比较取巧的方法。

因为类的静态成员和普通成员其实就一种区别,那就是静态成员本身没有this指针,所以静态成员属于类而不属于类对象。如果我们想在类的静态成员函数里面调用类的普通成员,只需要把类指针当做参数传入静态成员函数里面,静态成员函数可以使用这个指针调用类的普通成员,demo如下:

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
class A
{
 
private:
	string str;
public:
	
	void handler(string _str)
	{
		str = _str;
		cout<<str<<endl;
		
	}
	static void handler_static(void *p_A,string _str)
	{
	   A *_p =  static_cast<A*>(p_A);
	   _p->handler(_str);
	}
	
	void start()
	{
		void *p = this;
		string _str = "static";
		handler_static(p,_str);
	}
 
};
 
int _tmain(int argc, _TCHAR* argv[])
{
	A a;
	a.start();	
	getchar();
	return 0;
}

handler_static为静态成员函数,入参有两个,第一个为本身对象this指针,另一个为实际需要传入的参数。在这里实现的功能为使用类的静态成员函数给类的普通成员变量str赋值,如果不采用这种传入this指针间接调用的方式,那str也必须定义为静态的。 当然在这里看起来没多大意义,只是用一个handler成员函数接收this指针间接实现了功能。 但是在代码复杂的情况下这种方式可以避免过度破坏封装,在必须使用静态的地方(比如绑定回调)才定义静态成员,就不会有静态成员使用的连锁反应了。

Chapter3 静态成员、单例模式、友元、常量成员

原文链接

Chapter4 C++ 静态成员函数

原文链接:https://blog.csdn.net/feng19870412/article/details/124902096

成员函数也可以定义为静态的,在类中声明函数的前面加static就成了静态成员函数,例如:

    //定义静态成员函数;

    static long long get_number(){

        return number;

    }

和静态成员变量一样,静态成员函数是类的一部分,而不是对象的一部分。如果要在类外调用公用的静态成员函数,要用类名和域运算符“::”,例如:

student::get_number();

静态成员变量和静态成员函数,是属于类。那么,当用类来定义一个对象之后,静态成员变量和静态成员函数也属于对象,所以,也允许通过对象名调用静态成员函数。如下是程序测试代码:

在这里插入图片描述
程序运行结果如下:
在这里插入图片描述
可以看到,student类定义了get_number ()静态成员函数,那么,可以通过对象stud访问,也可以直接通过类名student来访问。

注意:当调用一个对象的成员函数(非静态成员函数)时,系统会把该对象的起始地址赋给成员函数的this指针。而静态成员函数并不属于某一个对象,它与任何对象都无关,因此静态成员函数没有this指针,既然它没有指向某一对象,就无法对一个对象中的非静态成员进行默认访问(即在引用成员变量时不指定对象名)。

可以说,静态成员函数与非静态成员函数的根本区别是:非静态成员函数有this指针,而“静态成员函数没有this指针”,由此决定了静态成员函数不能访问本类中的非静态成员。

静态成员函数可以直接引用本类中的静态成员变量,静态成员变量同样是属于类的,可以直接引用。在C++程序中,静态成员函数主要是用来访问静态成员变量,而不访问非静态成员。如下是一个例子,声明了volume(); 函数是 static 类型。那么,在该函数中只能够访问static类型的成员变量。

class Box

{

public:

    static int height;  // 定义为静态成员变量

    int width;

    int length;

    Box(int, int);

    static int volume();    //静态成员函数

};

int Box::volume()   //是static 类型的函数

{

    cout << height <<endl;  //合法,因为height 是static类型

    cout << width << endl;  //非法,因为width 不是static类型

}

       但是,并不是绝对不能引用本类中的非静态成员,只是不能进行默认访问,因为无法知道应该去找哪个对象。如果一定要引用本类的非静态成员,应该加对象名和成员运算符“.”,例如:

Box a(16, 11);  //定义一个对象a

int Box::volume()   //是static 类型的函数

{

    cout << a.width << endl;    //a 在这是定义的一个全局对象,在volume()函数中,可以访问指定的 a 对象的非静态成员变量。

    return 0;

}

只要 a 是一个已经定义的 Box 类对象,而且,它的作用域在 volume(); 内,那么,这个语句就合法。

总结

学习了静态成员变量和静态成员函数,我们了解到static关键字修饰的成员变量和成员函数,就属于“静态”类型。就是属于当前类的属性。

所以,当我们定义一个C++类的时候,静态成员就已经存在,它是属于这个类。所以,就可以直接通过类名来访问静态成员。

当“编译程序”的时候,它们就存在的,所以编译程序完成之后,不用定义类对象,就可以访问类的静态成员变量和静态成员函数,程序测试如下:
在这里插入图片描述
程序运行结果如下:
在这里插入图片描述
可以看到,在main()函数中没有定义任何对象,直接通过student类名访问静态的print()函数,而且,在print()函数中访问静态的成员变量。

所以,对于静态成员变量和静态成员函数,它们是属于一个类的,那么编译程序的时候,就对类进行了编译处理,所以,编译完程序之后,就可以使用类名和作用域运算符“::”来调用。

而对于非静态的成员变量和成员函数,是由一个对象来确定的,所以,必须在定义对象之后,才能够调用它们。

静态成员变量和静态成员函数,都是属于类的,可以使用类名和作用域运算符“::”来调用它们。当定义了类对象之后,也可以如同普通的成员变量和成员函数一样,使用对象和点运算符“.”来调用它们。

我们可以以“物体出现的时间先后顺序”来推理理解:静态和非静态成员之间的调用关系。根据,C++程序被编译之后,类先被编译出来,然后,才可以使用类来定义对象。所以,类先出现在对象之前。

假设现在只编译了类,不定义对象,所以,就通过类名来操作静态成员变量静态成员函数。如果此时,在静态成员函数中操作了非静态的成员变量,则出错,因为,非静态的成员变量是属于一个对象的,而此时,我们还没有使用类来定义对象,没有任何一个对象存在,所以,我们定义的静态成员函数,不能够调用非静态的成员变量和成员函数。

如果使用类来定义了一个对象,那么,这个对象的非静态成员变量和成员函数就产生了。而且,在编译了类之后,类的静态成员变量和静态成员函数已经生成,所以,可以使用对象的非静态成员函数来操作静态的成员变量和静态的成员函数。

读者要仔细分析,掌握好每一个知识点,然后,自己总结出对该知识点的理解。自己总结出来的知识,理解才深刻,才是自己掌握的知识。

Vite + Vue 3 + TypeScript 的项目中,你可以使用 Vue Router 来处理路由调用接口。 首先,确保你已经安装了 Vue Router 和 axios(或其他用于发送 HTTP 请求的库)。你可以使用以下命令进行安装: ``` npm install vue-router axios ``` 接下来,在你的项目中创建一个 `router` 文件夹,在其中创建一个 `index.ts` 文件。在该文件中,你可以配置你的路由。以下是一个示例: ```typescript import { createRouter, createWebHistory, RouteRecordRaw } from &#39;vue-router&#39;; import Home from &#39;@/views/Home.vue&#39;; import About from &#39;@/views/About.vue&#39;; const routes: Array<RouteRecordRaw> = [ { path: &#39;/&#39;, name: &#39;Home&#39;, component: Home, }, { path: &#39;/about&#39;, name: &#39;About&#39;, component: About, }, ]; const router = createRouter({ history: createWebHistory(), routes, }); export default router; ``` 在上面的示例中,我们定义了两个路由:`Home` 和 `About`。你可以根据你的需求进行修改和扩展。 然后,在你的入口文件(如 `main.ts`)中,引入使用你的路由: ```typescript import { createApp } from &#39;vue&#39;; import App from &#39;./App.vue&#39;; import router from &#39;./router&#39;; const app = createApp(App); app.use(router); app.mount(&#39;#app&#39;); ``` 现在,你可以在你的组件中使用 `vue-router` 进行路由导航和调用接口。例如,在 `Home.vue` 组件中,你可以这样使用: ```vue <template> <div> <h1>Home</h1> <button @click="fetchData">Fetch Data</button> </div> </template> <script> import axios from &#39;axios&#39;; export default { methods: { fetchData() { axios.get(&#39;/api/data&#39;).then((response) => { console.log(response.data); }).catch((error) => { console.error(error); }); }, }, }; </script> ``` 在上面的示例中,我们使用了 axios 发送 GET 请求来获取数据。你可以根据你的需求调整和扩展这个例子。 希望这能帮助到你!如果你有任何其他问题,请随时问我。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值