#include
using namespace std;
#define NYJ
int main()
{
#ifdef NYJ
cout << "ifdef NYJ" << endl;
#else
cout << "else" << endl;
#endif
}
print: ifdef NYJ
此时因为标识符“NYJ”被定义了,因此编译器会对#ifdef 后面的内容进行编译,以此同时跳过对@else后面内容的编译。
print: else
此时标识符“NYJ”未被定义,因此编译器不会对#ifdef后面的内容进行编译,与此同时对#else#endif后面内容进行编译。
注释:在以上代码中,#else并非必须出现。
typedef unsigned char uint8_t;
typedef int int32_t;
自定义的数据类型包括:结构体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"
//普通指针:
int a=2;
int* pointer;
pointer =&a;
//等同于
int a=2;
typedef int* pointer;
pointer p;
p=&a;
vector迭代器的定义
std::vector ::iterator it; //it能读写vector的元素
std::vector::const_iterator it;//it只能读vector的元素,不可以修改vector中的元素
用迭代器遍历Vector
for(it = vo.begin(); it!=vo.end();it++)
{
cout<< *it << endl;
}
//一定要带*
map迭代器的定义
map::iterator it; //it能读写map的元素
map::const_iterator it;//it只能读map的元素,不可以修改map中的元素
//用迭代器遍历map
for(itm = test_map.begin(); itm != test_map.end(); itm++)
{
cout <<"test_map = "<< (*itm).first <<" : " << (*itm).second <
List迭代器的定义
list::iterator it; //it能读写list的元素
list::const_iterator it;//it只能读list的元素,不可以修改list中的元素
用迭代器遍历list:
for(itl = num_list.begin(); itl != num_list.end(); itl++)
{
cout<<"num_list = " << *itl << endl;
}
功能:c_str() 函数可以将 const string* 类型 转化为 cons char* 类型
//标准库的string类提供了三个成员函数来从一个string得到c类型的字符数组
//主要介绍c_str
//c_str():生成一个const char*指针,指向以空字符终止的数组。
//这个数组应该是string类内部的数组
#include
//需要包含cstring的字符串
#include
using namespace std;
int main()
{
//更好的方法是将string数组中的内容复制出来 所以会用到strcpy()这个函数
char*c=new char[20];
string s =“1234";
//c_str()返回一个客户程序可读不可改的指向字符数组的指针,不需要手动释放或删除这个指针。
strcpy(c,s.c_str());
cout<
string中find()返回值是字母在母串中的位置(下标记录),如果没有找到,那么会返回一个特别的标记npos。(返回值可以看成是一个int型的数)
string s = "gon";
int pos = s.find("on");
cout<
print : 1
substr()是C++语言函数,主要功能是复制子字符串,要求从指定位置开始,并具有指定的长度。如果没有指定长度_Count或_Count+_Off超出了源字符串的长度,则子字符串将延续到源字符串的结尾。
string s = "gon";
s = s.substr(1);
cout<
print : on
str1.compare(str2)
小于返回负数,等于返回0,大于返回正数
除此之外,单个字符和string对象的比较还可以直接使用 “==”,相等的话返回值为1,否则为0
注意:但是,在比较两个C风格字符串时千万不能用 “==”,因为使用这个符号比较的是两个C风格字符串的首地址是否相等!
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
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
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 对象的成员
const修改全局变量是存储在全局区(即静态存储区),修饰局部变量时存储在栈区,const修饰的对象一旦创建后其值便不能再改变 ,类的数据成员只是声明,如果const修饰类的数据成员,那么必须要有构造函数来初始化这个const对象,其他情况必须在声明的地方就初始化。
class B{
public:
B():age(){}; //不初始化会报错
const int age;
};
int main(){
B b;
cout<
我们把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这个引用来修改它的值
1.常量指针
指向常量的指针不能用于改变其所指对象的值,只能使用常量指针(即对指针加const修饰)
常量指针都不可以去修改所指向的对象,常量引用也是不能修改所引用的对象,不管对象是不是常量都不能修改
const double pi = 3.1415; //pi是一个常量,它的值不能改变
double *ptr = π //错误:指向常量的指针必须是常量指针
const double *cptr = π //正确
*cptr = 42; //错误:常量指针不能修改对应的值
2.指针常量
const指针即指针存放的地址就不能再改变,我们称为指针常量,*在前
int errNumb = 0;
int *const curErr = &errNumb;// curErr将一直指向errNumb这块内存
const double pi = 3.14;
const double *const pip = π //从右向左依次解修饰符,首先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;
}
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;
}
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值,那个临时存储单元确实是个const int。
const int func1(int a);
int func1(int a);
//以上两个函数没有任何差异
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;
}
itoa()函数是"stdlib.h"里面的一个函数,其功能是将制定数转换成自己想要的进制数并存入指定的数组中。
char *itoa( int value, char *string,int radix);
itoa(321313,number,16);
cout<
头文件:
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
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();
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 中就是获取到的时间
vector为可变长数组(动态数组),定义的vector数组可以随时添加数值和删除元素。
在局部函数中开vector数组,是在堆空间里面开的,与开全局变量比较类似,所以经常见到在局部函数中开大容量数组
头文件
#include
初始化
vectornum; //定义了一个名为num的存int数据的一维数组
vectornum;//定义了一个名为num的存double数据的一维数组
vectornum;//node是结构体类型
//指定长度和初始值的初始化
vector v(n);//定义一个长度为n的数组,动态定义,不指定初值默认初值为0
vector v(n, 0);//所有的元素均为0
//注意:指定数组长度之后(指定长度后的数组就相当于正常的数组了)
//初始化中有多个元素
vector a{1, 2, 3, 4, 5};// 数组a中有五个元素
//拷贝初始化
vector a(n + 1, 0);
vector b(a);//两个数组中的类型必须相同,a和b都是长度为n+1,所有值都为0的数组
//方式一:单个访问,假设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 << " ";
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()返回的是最后一个元素的后一个位置的地址,不是最后一个元素的地址,所有容器均是如此
sort所需要的头文件
#include
正向排序:sort(c.begin(),c.end());
std::vector v{1,3,5,2};
std::sort(v.begin() , v.end());
for(auto vv : v)
cout<
print:1235
逆向排序:sort(vec.rbegin(), vec.rend())
std::vector v{1,3,5,2};
std::sort(v.rbegin() , v.rend());
for(auto vv : v)
cout<
print:5321
//头文件
#include
map特性: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 mp;
mp[1] = 1;
mp[2] = 2;
mp[3] = 3;
auto it = mp.begin();
while (it != mp.end())
{
cout<first<<" "<second<
print: 11 2 2 3 3
反向:
map mp;
mp[1] = 1;
mp[2] = 2;
mp[3] = 3;
auto it = mp.rbegin();
while (it != mp.rend())
{
cout<first<<" "<second<
print : 3 3 22 11
方法一:
mp["学习"] = "看书";
mp["玩耍"] = "打游戏";
方式二:make_pair<>插入元素构造键值对
mp.insert(make_pair("vegetable","蔬菜"));
方式三:pair< , >
mp.insert(pair("vegetable","蔬菜"));
方式四:
mp.insert({"hello","world"});
1.下标访问:
mp[1] = 12;
cout<
2.迭代器访问
map mp;
mp[1] = 1;
mp[2] = 2;
mp[3] = 3;
map::iterator it;
for(it = mp.begin(); it != mp.end(); it++)
{
// it是结构体指针访问所以要用 -> 访问
cout<first<<"-"<second<
3.智能指针访问
for(auto i : mp)
cout << i.first << " " << i.second << endl;//键,值
4.对指定单个元素访问
map::iterator it = mp.find('a');
cout << it -> first << " " << it->second << "\n";
在实现c++多态时会用到虚函数。虚函数使用的其核心目的是通过基类访问派生类定义的函数。所谓虚函数就是在基类定义一个未实现的函数名,为了提高程序的可读性,建议后代中虚函数都加上virtual关键字。
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; // 重写提示错误
};
定义一个函数为虚函数,不代表函数为不被实现的函数。
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。
定义一个函数为纯虚函数,才代表函数没有被实现。
定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 =0:
virtual void funtion1()=0
定义:包含纯虚函数的类称为抽象类。
抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7A9rye4-1681453869449)(D:\zwl\文档笔记\C++\C++随笔-md图片\image-20230414103434690.png)]
友元 friend 机制允许一个类授权其他的函数访问它的非公有成员。友元声明以关键字 friend 开头 ,它只能出现在类的声明中, 它们不受其在类体中的 public private 和 protected 区的影响.。
通俗来说就是就是让函数或者类作为另外一个类的朋友,则可以访问当前类的private或者protected类。
注意:
1.一个普通函数作为类的友元,那么在当前函数中就可以通过对象访问类的私有或者保护成员
注意:这个函数只能在外部定义,在当前类中引用即可
2.一个类的成员函数作为另外一个类的友元
注意:成员函数建议放在类外定义
3.一个类A作为另外一个类B的友元类,则A的所有的成员函数就可以访问B的私有数据成员或者保护
友元分为外部函数友元, 成员函数友元,类友元。
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);
}
1.不具有对称性
A 是 B 的友元, 并不意味着 B 是A的友元。
2.不具有传递性
A是B的友元, B是C的友元, 但A不是C的友元。
3.不具有继承性
Base 类型继承 Object类型, 如果Object 类型是A的友元,但Base类型不是A友 元
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)
-静态成员函数是类的一个特殊的成员函数
-静态成员函数属于整个类所有,没有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
数
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)
-静态成员函数是类的一个特殊的成员函数
-静态成员函数属于整个类所有,没有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