C++学习随笔

1. ⭐️#ifdef #ifndef #else #endif⭐️

1.1 #ifdef: ifdef = if define

1.1.1 标识符被定义(出现#define ***)

#include<iostream>
using namespace std;
#define NYJ
 
int main()
{
#ifdef NYJ
	cout << "ifdef NYJ" << endl;
#else
	cout << "else" << endl;
#endif
}

print: ifdef NYJ

此时因为标识符“NYJ”被定义了,因此编译器会对#ifdef 后面的内容进行编译,以此同时跳过对@else后面内容的编译。

1.1.2 标识符未被定义(没有出现#define ***)

print: else

此时标识符“NYJ”未被定义,因此编译器不会对#ifdef后面的内容进行编译,与此同时对#else#endif后面内容进行编译。

注释:在以上代码中,#else并非必须出现。

1.2 #ifndef: ifndef = if not define

2. ⭐️Typedef用法⭐️

2.1 typedef给基本数据类型取别名

typedef unsigned char           uint8_t;
typedef int                     int32_t;

2.2 typedef给自定义数据类型取别名

自定义的数据类型包括:结构体struct name{ }; 、共用体unit name { };、枚举enum { };

struct students
{
	char sex;
	char name[120];
	int ages;
}std;
std.name[20]="wujunwu"
用typedef定义:
struct students
{
	char sex;
	char name[120];
	int ages;
};
typedef struct students std;
std.name[20]="wujunwu"

2.3 typedef为数组取别名

//普通指针:
int a=2;
int* pointer;
pointer =&a;

//等同于
int a=2;
typedef int* pointer;
pointer p;
p=&a;

3. ⭐️迭代器iterator的用法⭐️

3.1 Vector的迭代器

vector迭代器的定义

std::vector<int> ::iterator it;     //it能读写vector<int>的元素
std::vector<int>::const_iterator it;//it只能读vector<int>的元素,不可以修改vector<int>中的元素

用迭代器遍历Vector

for(it = vo.begin(); it!=vo.end();it++)
{
    cout<< *it << endl;
}
//一定要带*

3.2 map迭代器

map迭代器的定义

map<char,string>::iterator it;      //it能读写map<char,string>的元素
map<char,string>::const_iterator it;//it只能读map<char,string>的元素,不可以修改map<char,string>中的元素
//用迭代器遍历map
for(itm = test_map.begin(); itm != test_map.end(); itm++)
{
    cout <<"test_map = "<< (*itm).first <<" : " << (*itm).second <<endl;
}

3.3 List迭代器

List迭代器的定义

list<int>::iterator it;      //it能读写list<int>的元素
list<int>::const_iterator it;//it只能读list<int>的元素,不可以修改list<int>中的元素
用迭代器遍历list:
for(itl = num_list.begin(); itl != num_list.end(); itl++)
{
    cout<<"num_list = " << *itl << endl;
}

4. ⭐️string日常用法⭐️

4.1 c_str()的用法

功能:c_str() 函数可以 const string* 类型 转化为 cons char* 类型

//标准库的string类提供了三个成员函数来从一个string得到c类型的字符数组
//主要介绍c_str
//c_str():生成一个const char*指针,指向以空字符终止的数组。
//这个数组应该是string类内部的数组
#include <iostream>
//需要包含cstring的字符串
#include <cstring>
using namespace std; 

int main()
{
	//更好的方法是将string数组中的内容复制出来 所以会用到strcpy()这个函数
    char*c=new char[20];
	string s =“1234";

	//c_str()返回一个客户程序可读不可改的指向字符数组的指针,不需要手动释放或删除这个指针。
    strcpy(c,s.c_str());
	cout<<c<<endl; 
    s="abcd";
    cout<<c<<endl; 
}

4.2 find()的用法

string中find()返回值是字母在母串中的位置(下标记录),如果没有找到,那么会返回一个特别的标记npos。(返回值可以看成是一个int型的数)

string s = "gon";
int pos = s.find("on");
cout<<pos;

print : 1

4.3 subStr()

substr()是C++语言函数,主要功能是复制子字符串,要求从指定位置开始,并具有指定的长度。如果没有指定长度_Count或_Count+_Off超出了源字符串的长度,则子字符串将延续到源字符串的结尾。

string s = "gon";
s = s.substr(1);
cout<<s;

print : on

4.4 string之间相比较

str1.compare(str2)

小于返回负数,等于返回0,大于返回正数

除此之外,单个字符和string对象的比较还可以直接使用 “==”,相等的话返回值为1,否则为0

注意:但是,在比较两个C风格字符串时千万不能用 “==”,因为使用这个符号比较的是两个C风格字符串的首地址是否相等!

5. ⭐️C++字符串数组操作⭐️

5.1 截取从某位置开始指定长度子字符串方法

strncpy(dest,src,len);

char dest[4] = {""};
char src[] = {"123456789"};
strncpy(dest, src, 3);
puts(dest);
//输出结果为 123

char dest[4] = {""};
char src[] = {"123456789"};
strncpy(dest, src+3, 3);
puts(dest);
//src+3 就可以实现将 src中从第4个字符开始复制n个字符给 dest 了
//输出结果为 456

5.2 字符串数组的比较

int strcmp ( const char * str1, const char * str2 ); 小于返回负数,等于返回0,大于返回正数

char  ch[10] = "5";
int aaa = str.compare("5");
int result = strcmp(ch,"5");
if (result == 0)
{
    std::cout<<"true";
}

print: true

6. ⭐️C++关键字⭐️

6.1 mutable

mutable 英文中表示,易变的,不定的;性情不定的,而在代码中表示 可变数据成员。由const修饰的成员函数中,不能对成员变量进行修改,因为隐藏的形参是 const 属性的。

而实际开发过程中,我们可能需要对某些成员变量进行修改,就需要用到 mutable。

class constA
{
public:

	constA(int a):m_a(a){}
	void funcA() const { cout <<++m_a<< endl;}
    
    mutable int m_a;

};

int main()
{
    constA ab(10); ab.funcA();

	const constA abc(11); abc.funcA();

	abc.m_a = 15; abc.funcA(); 
}

print : 11 12 16

这里可以看到在const的成员函数中,可以对mutable修饰的变量进行修改。 而对于const对象,它的成员变量依然可以进行修改。

总结一句话:一个 可变数据成员,永远不会是const,即使它是const 对象的成员

6.2 const

6.2.1 const修饰符的作用

  1. const类型定义: 指明变量或对象的值是不能被更新,引入目的是为了取代预编译指令
  2. 可以保护被修饰的东西,防止意外的修改,增强程序的健壮性
  3. 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高
  4. 可以节省空间,避免不必要的内存分配

6.2.2 初始化和const

const修改全局变量是存储在全局区(即静态存储区),修饰局部变量时存储在栈区,const修饰的对象一旦创建后其值便不能再改变 ,类的数据成员只是声明,如果const修饰类的数据成员,那么必须要有构造函数来初始化这个const对象,其他情况必须在声明的地方就初始化。

class B{
public:
    B():age(){}; //不初始化会报错

    const int age;
};

int main(){
	B b;
	cout<<b.age;
}

6.2.3 const和引用

我们把const修饰的引用称为”常用引用“,常量引用不能直接修改所引用的对象

const int ci = 1024;//ci是一个int型的常量
const int &r1 = ci;//正确,r1是一个常量引用,并且r1本身也是一个常量
r1 = 42;//错误,引用被const限制了,不能修改所引用对象的值了
int &r2 = ci;//错误,试图让一个非常量引用指向一个常量对象

常量引用所引用的对象可能是一个非常量

int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0;//r1,r2都是i的引用
cout << "i " << i << endl;//0
cout << "r1 " << r1 << endl;//0
cout << "r2 " << r2 << endl;//0
r2 = 10 //错误,不能通过r2来修改i的值,因为有了const的限定

常量引用只是对引用可参与的操作做出了限定,对于引用的对象本身是不是常量未作限定,因此上面的代码中i不是常量,可以r1这个引用来修改它的值

6.2.4 const和指针

1.常量指针

指向常量的指针不能用于改变其所指对象的值,只能使用常量指针(即对指针加const修饰)

常量指针都不可以去修改所指向的对象,常量引用也是不能修改所引用的对象,不管对象是不是常量都不能修改

const double pi = 3.1415;  //pi是一个常量,它的值不能改变
double *ptr = &pi; //错误:指向常量的指针必须是常量指针
const double *cptr = &pi; //正确
*cptr = 42; //错误:常量指针不能修改对应的值

2.指针常量

const指针即指针存放的地址就不能再改变,我们称为指针常量,*在前

int errNumb = 0;
int *const curErr = &errNumb;// curErr将一直指向errNumb这块内存
const double pi = 3.14;
const double *const pip = &pi; //从右向左依次解修饰符,首先pip是一个常量,然后是*说明pip是一个常量指针,
//再然后是double说明是指向double的指针,最后是const说明这个double不能变,即指向的对象是一个常量的double

两者对比:

int main(){
    int a = 2;
    int b = 3;
    //常量指针
    const int * pInt = &a;
    pInt = &b; //*pInt = 3;
    //*pInt = 5; 会报错,不能修改指针的值

    //指针常量
    int * const pInt1 = &a;
    //pInt1 = &b;会报错。不能修改指针地址
    *pInt1 = 5; //此时a = 5;

    return 0;
}

6.2.5 const 形参和实参

const修饰形参

void func(const int i);//传入的i可以是const,也可以是非const,但是func不能修改i
void func(int i);//错误,重复定义

如果形参是可变的,那么传进来的值也必须是可变的

void hello(int& age){};
void world(const int& age){
    //age = 5; 报错,不能对age进行修改,因为有const修饰
}

int main(){
    
    const int age1 = 5;
    int age = 6;
    
    hello(age);
    hello(age1);//报错,形参变量,实参也必须是变量,ci是常量

    world(age1);
    world(age);
    
    return 0;
}

6.2.6 const返回值

如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值,那个临时存储单元确实是个const int。

const int func1(int a);
int func1(int a);
//以上两个函数没有任何差异

6.2.7 const函数

const去限定函数只允许发生在类的成员函数中,表明该函数不会修改类的成员变量,const放在成员函数的末尾,声明和定义都要加。

1.只能限定类的成员函数
2.const限定后,该成员函数不允许修改类的数据成员
3.也不允许调用非const函数,即使该函数没有修改类的数据成员,只要没有声明成const,就不能调用 
class Stack
{
public:
 void Push(int elem);
 int Pop(void);
 int GetCount(void) const; // const 成员函数
private:
 int m_num;
 int m_data[100];
};
int Stack::GetCount(void) const
{
 ++m_num; // 编译错误,企图修改数据成员m_num
 Pop(); // 编译错误,企图调用非const 函数
 return m_num;
}

7. ⭐️itoa()函数⭐️

itoa()函数是"stdlib.h"里面的一个函数,其功能是将制定数转换成自己想要的进制数并存入指定的数组中。

char *itoa( int value, char *string,int radix);

itoa(321313,number,16);
 cout<<number<<endl;
//输出:4e721

8. ⭐️C++单例模式⭐️

8.1 懒汉式

头文件:

1.class App{
2.	public:
3.	    App();
4.	    ~App(){}
5.	    static App* getInstance();
6.	private:
7.	    static App* a_instance;
8.};

Cpp:

#include "APP.h"
#include <iostream>

App* App::a_instance = nullptr; //注意:一定要定义这个变量
App::App() {}

App* App::getInstance() {

    if (a_instance == NULL)
    {
        a_instance = new App();
    }
    return a_instance;
}

Main.cpp

int age = App::getInstance()->getAge();

9. ⭐️按某种格式获取当前时间⭐️

    time_t now = time(0);
    struct tm *timeinfo = nullptr;
    uint8_t nowdate[80] = "";
    timeinfo = localtime(&now);
    strftime((char*)nowdate,80,"%Y-%m-%d %H:%M:%S",timeinfo);
    printf("%s\n",nowdate); //nowdate 中就是获取到的时间

10. ⭐️vector(动态数组)⭐️

10.1 介绍

10.1.1 数组初始化方法

vector为可变长数组(动态数组),定义的vector数组可以随时添加数值和删除元素。

在局部函数中开vector数组,是在堆空间里面开的,与开全局变量比较类似,所以经常见到在局部函数中开大容量数组

头文件

#include <vector>

初始化

vector<int>num; //定义了一个名为num的存int数据的一维数组
vector<double>num;//定义了一个名为num的存double数据的一维数组
vector<node>num;//node是结构体类型

//指定长度和初始值的初始化
vector<int> v(n);//定义一个长度为n的数组,动态定义,不指定初值默认初值为0
vector<int> v(n, 0);//所有的元素均为0
//注意:指定数组长度之后(指定长度后的数组就相当于正常的数组了)

//初始化中有多个元素
vector<int> a{1, 2, 3, 4, 5};// 数组a中有五个元素

//拷贝初始化
vector<int> a(n + 1, 0);
vector<int> b(a);//两个数组中的类型必须相同,a和b都是长度为n+1,所有值都为0的数组

10.1.2 元素的访问方法

//方式一:单个访问,假设num数组中已经有了5个元素
cout << num[4] << "\n";  //输出第五个数据
//一二维可变数组和普通数组的访问方法一样

//方式二:遍历
for(int i = 0; i < num.size(); i++)
	cout << num[i] << " ";//下标范围在[0,num.size()),前开后闭

//方式三:智能指针
for(auto i : num)
	cout << i << " ";

10.2 Vector的方法函数

c.front()				返回第一个数据 O(1)
c.back()				返回最后一个数据 O(1)
c.pop_back()			删除最后一个数据 O(1)
c.push_back(element)	在尾部加一个数据 O(1)
c.size()				返回实际数据个数(unsigned类型) O(1)
c.clear()				清除元素个数 O(N),N为元素个数
c.resize(n,v)			改变数组大小为n,n个空间数值赋为v,如果没有默认赋值为0
c.insert(it,x)			向任意迭代器it插入一个元素x O(N)
例:c.insert(c.begin()+2,-1)	将-1插入c[2]的位置
c.erase(first,last)		删除[first,last)的所有元素
c.begin()				返回首元素的迭代器(通俗来说就是地址)
c.end()					返回最后一个元素后一个位置的迭代器(地址)
c.empty()				判断是否为空,为空返回真,反之返回假
reverse(c.begin(),c.end())               翻转函数

注意: end()返回的是最后一个元素的后一个位置的地址,不是最后一个元素的地址,所有容器均是如此

10.3 使用Vector

10.3.1 sort()排序

sort所需要的头文件

#include <algorithm>

正向排序:sort(c.begin(),c.end());

std::vector<int> v{1,3,5,2};
std::sort(v.begin() , v.end());
for(auto vv : v)
   cout<<vv;

print:1235

逆向排序:sort(vec.rbegin(), vec.rend())

std::vector<int> v{1,3,5,2};
std::sort(v.rbegin() , v.rend());
for(auto vv : v)
    cout<<vv;

print:5321

11. ⭐️Map⭐️

11.1 介绍

//头文件
#include<map>
//初始化定义
map<string,string> mp;
map<string,int> mp;
map<int,node> mp;//node是结构体类型

map特性:map会按照键的顺序从小到大自动排序

11.2 Map的函数方法

mp.find(key) 返回键为key的映射的迭代器 O(logN) 注意:用find函数来定位数据出现位置,它返回一个迭代器。当数据存在时,返回数据所在位置的迭 代器,数据不存在时,返回mp.end()
mp.erase(it) 删除迭代器对应的键和值O(1)
mp.erase(key) 根据映射的键删除键和值 O(logN)
mp.erase(first,last) 删除左闭右开区间迭代器对应的键和值 O(last-first)
mp.size() 返回映射的对数 O(1)
mp.clear() 清空map中的所有元素 O(N)
mp.insert() 插入元素,插入时要构造键值对
mp.empty() 如果map为空,返回true,否则返回false
mp.begin() 返回指向map第一个元素的迭代器(地址)
mp.end() 返回指向map尾部的迭代器(最后一个元素的下一个地址)
mp.rbegin() 返回指向map最后一个元素的反向迭代器(地址)
mp.rend() 返回指向map第一个元素前面(上一个)的反向迭代器(地址)
mp.count(key) 查看元素是否存在,因为map中键是唯一的,所以存在返回1,不存在返回0
mp.lower_bound() 返回一个迭代器,指向键值>= key的第一个元素
mp.upper_bound() 返回一个迭代器,指向键值> key的第一个元素

注意:查找元素是否存在时,可以用:1️⃣mp.find() 2️⃣ mp.count()

使用迭代器进行正反向的遍历

正向

map<int,int> mp;
mp[1] = 1;
mp[2] = 2;
mp[3] = 3;
auto it = mp.begin();
while (it != mp.end())
{
    cout<<it->first<<" "<<it->second<<endl;
    it ++;
}

print: 11 2 2 3 3

反向

map<int,int> mp;
mp[1] = 1;
mp[2] = 2;
mp[3] = 3;
auto it = mp.rbegin();
while (it != mp.rend())
{
    cout<<it->first<<" "<<it->second<<endl;
    it ++;
}

print : 3 3 22 11

11.3 添加元素

方法一:

mp["学习"] = "看书";
mp["玩耍"] = "打游戏";

方式二:make_pair<>插入元素构造键值对

mp.insert(make_pair("vegetable","蔬菜"));

方式三:pair< , >

mp.insert(pair<string.string>("vegetable","蔬菜"));

方式四:

mp.insert({"hello","world"});

11.4 访问元素

1.下标访问:

mp[1] = 12;
cout<<mp[1];
//print:12

2.迭代器访问

map<int,int> mp;
mp[1] = 1;
mp[2] = 2;
mp[3] = 3;
map<int,int>::iterator it;
for(it = mp.begin(); it != mp.end(); it++)
{
    // it是结构体指针访问所以要用 -> 访问
    cout<<it->first<<"-"<<it->second<<endl;
    //*it是结构体变量 访问要用 . 访问
    //cout<<(*it).first<<" "<<(*it).second;
}

3.智能指针访问

for(auto i : mp)
cout << i.first << " " << i.second << endl;//键,值

4.对指定单个元素访问

map<char,int>::iterator it = mp.find('a');
cout << it -> first << " " <<  it->second << "\n";

12.⭐️虚函数–Virtual⭐️

12.1 虚函数的定义

在实现c++多态时会用到虚函数。虚函数使用的其核心目的是通过基类访问派生类定义的函数。所谓虚函数就是在基类定义一个未实现的函数名,为了提高程序的可读性,建议后代中虚函数都加上virtual关键字。

12.2 override,final

  • override:保证在派生类中声明的重载函数,与基类的虚函数有相同的签名;重写虚函数建议加上override

    class Base {
    public:
        virtual void Show(int x); // 虚函数
    };
     
    class Derived : public Base {
    public:
        virtual void Show(int x) const override; // const 属性不一样,新的虚函数 
    };
    
  • final:阻止类的进一步派生 和 虚函数的进一步重写。如果不希望某个类被继承,或不希望某个虚函数被重写,则可以在类名和虚函数后加上 final 关键字,加上 final 关键字后,再被继承或重写,编译器就会报错。

class Base {
public:
    virtual void Show(int x) final; // 虚函数
};
 
class Derived : public Base {
public:
    virtual void Show(int x) override; // 重写提示错误  
};

12.3 虚函数与纯虚函数

定义一个函数为虚函数,不代表函数为不被实现的函数。

定义他为虚函数是为了允许用基类的指针来调用子类的这个函数

定义一个函数为纯虚函数,才代表函数没有被实现

定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。

12.3.1 纯虚函数:

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 =0:

virtual void funtion1()=0

12.3.2 抽象类

定义:包含纯虚函数的类称为抽象类。

抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。

13. c++函数重载和重写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7A9rye4-1681453869449)(D:\zwl\文档笔记\C++\C++随笔-md图片\image-20230414103434690.png)]

14.友元

14.1 定义

友元 friend 机制允许一个类授权其他的函数访问它的非公有成员。友元声明以关键字 friend 开头 ,它只能出现在类的声明中, 它们不受其在类体中的 public private 和 protected 区的影响.。

通俗来说就是就是让函数或者类作为另外一个类的朋友,则可以访问当前类的private或者protected类。

注意:

1.一个普通函数作为类的友元,那么在当前函数中就可以通过对象访问类的私有或者保护成员
  注意:这个函数只能在外部定义,在当前类中引用即可
2.一个类的成员函数作为另外一个类的友元
  注意:成员函数建议放在类外定义
3.一个类A作为另外一个类B的友元类,则A的所有的成员函数就可以访问B的私有数据成员或者保护

14.2 友元的分类

友元分为外部函数友元, 成员函数友元,类友元。

1.外部函数友元

class A{
public:
    A(int x):m_age(x){};
    friend int getAge(const A& a);
private:
    int m_age;
};

int getAge(const A& a)
{
    return a.m_age;//如果没有加友元,此时a不能访问m_age.
}

该函数只能在类的外部定义

一个外部函数作为多个类的友元函数,需要前向引用声明。

class B;//前向引用声明
class C;
class A
{
public:
	A(int i = 0) :m_i(i) {}
	friend int Sum(A& a, B& b, C& c);
private:
	int m_i;
};
class B
{
public:
	B(int j = 0) :m_j(j) {}
	friend int Sum(A& a, B& b,C& c);
private:
	int m_j;
};
class C
{
public:
	C(int k = 0) :m_k(k) {}
	friend int Sum(A& a, B& b,C&c);
private:
	int m_k;
};
int Sum(A& a, B& b,C&c)
{
	return a.m_i + b.m_j+c.m_k;
}
void main()
{
	A a(10);
	B b(20);
	C c(30);
	cout << Sum(a, b, c) << endl;
}

顺序不是第一个的类必须向前引用声明, 否则会出现以下错误:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CeZiNW0N-1681453869449)(D:\zwl\文档笔记\C++\C++随笔-md图片\47d3a5038f414627834fafc0f3abdc29.png)]

2.成员函数友元

一个类的成员函数成为了其他类的友元函数,**一般情况下,把声明函数的类放在前面,**函数的定义写在类的外面。

class B;
class A {
public:
    A() {};
    string getName(B& b);
};

class B {
public:
    B(string name) :m_name(name) {}
    friend string A::getName(B& b);
private:
    string m_name;
};

string A::getName(B& b) {
    return b.m_name;
}
int main() {
    A a;
    B b("jack");
    string name = a.getName(b);

    return 0;
}

int main() {
    A a;
    B b("jack");
    string name = a.getName(b);

    return 0;
}

3.类友元

类A作为类B的友元函数,(在B中写friend class A的意思就是:告诉B,A是它的friend),所以类A的成员函数可以访问类B中的所有函数,只需在类A中声明。

class B;
class A
{
public:
	A(int a = 0) :m_a(a) {}
	void print(B& b);
private:
	int m_a;
};
class B
{
public:
	B(int b=0):m_b(b){}
	friend class A;
private:
	int m_b;
};
 
void A::print(B& b)
{
	cout << "a::print:" << b.m_b << endl;
}

void main()
{
	A a(2);
	B b(10);
	a.print(b);
}

14.3 友元的特点

1.不具有对称性

A 是 B 的友元, 并不意味着 B 是A的友元。

2.不具有传递性

A是B的友元, B是C的友元, 但A不是C的友元。

3.不具有继承性

Base 类型继承 Object类型, 如果Object 类型是A的友元,但Base类型不是A友 元

15.C++静态变量和静态函数

15.1 静态变量

class A {
    
public:
    A() {};
    static  int f ;
};

//类的静态成员变量需要在类外分配内存空间
int A::f = 5;//要加这一句定义,不然会报错 

int main() {
    A a;
    A aa;
    a.f = 3;
    //所有对象共享类的静态成员变量
    cout << a.f << endl;     //print = 3
    cout << aa.f << endl;	 //print = 3  

    return 0;
}

除了在头文件中定义以外,应该在类的生命之外重新定义一次。否则会报错。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZFeVY51M-1681453869450)(D:\zwl\文档笔记\C++\C++随笔-md图片\image-20230414135359171.png)]

-静态成员变量属于整个类所有
-静态成员变量的生命期不依赖于任何对象,为程序的生命周期
-可以通过类名直接访问公有静态成员变量
-所有对象共享类的静态成员变量
-可以通过对象名访问公有静态成员变量
-静态成员变量需要在类外单独分配空间
-静态成员变量在程序内部位于全局数据区 (Type className::VarName = value)

15.2 静态成员函数

-静态成员函数是类的一个特殊的成员函数
-静态成员函数属于整个类所有,没有this指针
-静态成员函数只能直接访问静态成员变量和静态成员函数
-可以通过类名直接访问类的公有静态成员函数
-可以通过对象名访问类的公有静态成员函数
-定义静态成员函数,直接使用static关键字修饰即可

class A {
private:
    static int value;
public:
    A() { value++; };
    static int getValue()
    {
        return value;
    }
};

int A::value = 0;

int main() {
  
    A a;
    std::cout << A::getValue() << std::endl;
    A b;
    std::cout << b.getValue() << std::endl;
    return 0;
}

输出:1

15.1 静态变量

class A {
    
public:
    A() {};
    static  int f ;
};

//类的静态成员变量需要在类外分配内存空间
int A::f = 5;//要加这一句定义,不然会报错 

int main() {
    A a;
    A aa;
    a.f = 3;
    //所有对象共享类的静态成员变量
    cout << a.f << endl;     //print = 3
    cout << aa.f << endl;	 //print = 3  

    return 0;
}

除了在头文件中定义以外,应该在类的生命之外重新定义一次。否则会报错。

[外链图片转存中…(img-ZFeVY51M-1681453869450)]

-静态成员变量属于整个类所有
-静态成员变量的生命期不依赖于任何对象,为程序的生命周期
-可以通过类名直接访问公有静态成员变量
-所有对象共享类的静态成员变量
-可以通过对象名访问公有静态成员变量
-静态成员变量需要在类外单独分配空间
-静态成员变量在程序内部位于全局数据区 (Type className::VarName = value)

15.2 静态成员函数

-静态成员函数是类的一个特殊的成员函数
-静态成员函数属于整个类所有,没有this指针
-静态成员函数只能直接访问静态成员变量和静态成员函数
-可以通过类名直接访问类的公有静态成员函数
-可以通过对象名访问类的公有静态成员函数
-定义静态成员函数,直接使用static关键字修饰即可

class A {
private:
    static int value;
public:
    A() { value++; };
    static int getValue()
    {
        return value;
    }
};

int A::value = 0;

int main() {
  
    A a;
    std::cout << A::getValue() << std::endl;
    A b;
    std::cout << b.getValue() << std::endl;
    return 0;
}

输出:1

​ 2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值