这个博客是我这个小白程序员工作中学习的记录,首要作用是作为自己的笔记,如果能帮到他人就更好,如果有大神帮忙指点那就更更更好!!
1.virtual关键字,基类指针调用,如果不加virtual无法调用派生类重写的。
#include <iostream>
using namespace std;
class A
{
public:
A();
~A();
virtual void f()
{
cout << "A" << endl;}
};
A::A(){}
A::~A(){}
class B : public A
{
public:
B();
~B();
void f()
{
cout << "B" << endl;
}
};
B::B(){}
B::~B(){}
void main()
{
A* a = new B;
a->f();
system("pause");
}
加virtual输出B,不加virtual输出A
2.在函数后加const的意义
const char getData {return this->letter;}
char getData() const {return this->letter;}
- 前面使用const 表示返回值为const
-
后面加 const表示函数不可以修改class的成员
3.C++11强枚举类型enum class
在标准C++中,枚举类型不是类型安全的。枚举类型被视为整数,这使得两种不同的枚举类型之间可以进行比较。C++03 唯一提供的安全机制是一个整数或一个枚举型值不能隐式转换到另一个枚举别型。 此外,枚举所使用整数类型及其大小都由实现方法定义,皆无法明确指定。 最后,枚举的名称全数暴露于一般范围中,因此C++03两个不同的枚举,不可以有相同的枚举名。
(好比 enum Side{ Right, Left }; 和 enum Thing{ Wrong, Right }; 不能一起使用。)
C++11 引进了一种特别的 "枚举类",可以避免上述的问题。使用 enum class 的语法来声明:
enum class Enumeration{ Val1, Val2, Val3 = 100, Val4 /* = 101 */,};
此种枚举为类型安全的。枚举类型不能隐式地转换为整数;也无法与整数数值做比较。 (表示式 Enumeration::Val4 == 101 会触发编译期错误)。
#include <iostream>
using namespace std;
enum class Enumeration1
{
Val1, //0
Val2, //1
Val3 = 100, //100
Val4 //101
};
4.__declspec(dllexport)
https://www.jianshu.com/p/ea45468f25f1
5.inline
https://www.cnblogs.com/Cqlismy/p/11153449.html
6.void作形参
c语言中的void 基本上就是两个意思:
无,没有
任意类型
这两个意思,与void出现的代码位置和修饰变量相关:
函数返回值位置,如:
void func(int i) ; //表示func()函数没有返回值,void不可省
函数参数位置,如:
void func( void ) ; //表示func()函数即没有返回值,也没有参数。这里括号中的void可省。
修饰指针变量(一般多用于函数参数)
int func( void *a, void *b) ; //表示形参a b可以是任意类型指针变量
修饰常量(一般用于宏定义)
#define NULL ((void*)0) //这里NULL从数值上讲就是0,但这样写,可以表明NULL是个(任意)指针类型数据
7. __FILE__、__FUNCTION__、__LINE__
__FILE__&__FUNCTION__ 类型为 char*
__LINE__ 类型为int
8.substr()
0. 用途:一种构造string的方法
1. 形式:s.substr(pos, n)
2. 解释:返回一个string,包含s中从pos开始的n个字符的拷贝(pos的默认值是0,n的默认值是s.size() - pos,即不加参数会默认拷贝整个s)
3. 补充:若pos的值超过了string的大小或者为负,则substr函数会抛出一个out_of_range异常;若pos含有小数则会自动向下取整;若pos+n的值超过了string的大小,则substr会调整n的值,只拷贝到string的末尾(包含n等负数)
9.std::string::find_first_of
string (1) | |
---|---|
c-string (2) | |
buffer (3) | |
character (4) | |
在字符串中搜索与参数中指定的任何字符匹配的第一个字符。
当指定了pos时,搜索只包括pos或pos之后的字符,忽略pos之前可能出现的任何字符。
注意,只要序列中的一个字符匹配(而不是所有字符)就足够了
10.dynamic_cast
将基类指针或引用转换为派生类的指针或引用
基类必须有虚函数,否则编译错误
例:
class Base
{
public:
Base(){}
public:
virtual ~Base(){}
}
class Derived : public Base
{
public:
Derived();
public:
~Derived();
}
void main()
{
Base* pt1 = new Base();
Derived* pt = dynamic_cast<Derived>(pt1);
}
11.define中的三个特殊符号:#,##,#@
#define Conn(x,y) x##y
#define ToChar(x) #@x
#define ToString(x) #x
void main()
{
// ## x连接y
int n = Conn(123,456); // n = 123456
char* str = Conn("abc", "efg"); // str = "abcdef"
// #@ 给x加单引号返回const char
char a = ToChar(1); // a = '1'
// # 给x加双引号
char* str = ToString(123456); // str = "123456"
}
12.虚
参考下面代码,子类存在函数func那么父类所有名为func的函数(包括所有重载的函数,返回值不同的,被const修饰等区别)都将被子类的函数func覆盖(overwrite---可理解为覆盖,子类覆盖了父类所有同名函数),所以Derived对象并不存在func(int x)函数
#include<iostream>
class Base{
public:
virtual void func(){}
virtual void func(int x){}
};
class Derived:public Base{
public:
//overwrite父类所有名为func的函数
virtual void func(){}
};
int main()
{
Base* pa = new Derived();
pa->func();
pa->func(10);
delete pa;
Derived* pb = new Derived();
pb->func();
//这个调用将会报错,Derived类func()覆盖了所有Base类中名为func的函数
//所以Derived类并没有与func(int x)这个函数
pb->func(10);
delete pb;
}
13.2020-08-19 今天把全局变量当做静态变量,导致Bug久久未发现,记录在此,以此为戒!
14.C++静态成员变量
C++静态成员变量:
- 必须在外部定义和赋值;
- 不能在 main() 函数中定义
- 不能在类的构造函数中定义
- 必须要定义静态变量,否则该变量没有内存空间(类中只是申明) ;
- 类本身可以直接调用静态变量
- 静态变量依旧符合public、private、protect特性
参考链接:https://www.runoob.com/w3cnote/cpp-static-usage.html
15.C++静态成员方法
1、静态方法可以被类直接调用,但是同静态变量一样符合public、private、protect特性
2、静态成员函数不可以调用类的非静态成员。因为静态成员函数不含this指针。
3、静态成员函数不可以同时声明为 virtual或后缀const 函数。因为virtual就是用来区分是哪个对象调用了他,与静态方法矛盾。而后缀const是用来修饰this指针的,静态变量中不包含this指针。
16.函数指针
int sum(int x, int y)
{
return x + y;
}
int main()
{
int (*p)(int, int) = sum;
int x = (*p)(1,2); //!< 调用传参
}
17.拷贝构造函数
调用时机:
①函数的参数为类对象
②函数返回值是类对象
③通过对象初始化对象
A a(1);
A b(a);
18.断言
如果它的条件返回错误,则终止程序执行(debug模式,用于调试)
#include "assert.h"
#include "crtdbg.h"
int main(int argc, char* argv[])
{
int i=5;
assert( i >= 6); //包含在 assert.h 头文件中 此函数会将错误信息显示在命令行中,在命令行中 显示出错的语句和行数
_ASSERTE( i >= 6); //包含在 crtdbg.h 头文件中 此宏会显示出在此宏中的语句
ASSERT( i >= 6); //包含在 crtdbg.h 头文件中 此宏不会显示在此宏中的语句,而只是显示出错的行数
}
在调试结束后,可以通过在包含#include 的语句之前插入 #define NDEBUG 来禁用assert()调用
#define NDEBUG
#include "assert.h"
19.静态变量只初始化一次
#include<iostream>
using namespace std;
int func()
{
static bool temp = true;//静态变量初始化一次
if (temp)
{
cout << "Test" << endl;
}
temp = false;
return 0;
}
int main()
{
while (1)
{
func();
}
}
20.inline 内联函数
21.特殊字符以UTF-8输出
字符串前面加u8,source文件以UTF-8格式保存
std::string str = u8"Test";
22.友元函数和友元类
友元类:
一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。在类定义中声明友元类的写法如下:
class CCar
{
private:
int price;
friend class CDriver; //声明 CDriver 为友元类
};
class CDriver
{
public:
CCar myCar;
void ModifyCar() //改装汽车
{
myCar.price += 1000; //因CDriver是CCar的友元类,故此处可以访问其私有成员
}
};
int main()
{
return 0;
}
友元函数:
#include <iostream>
using namespace std;
class Box
{
double width;
public:
friend void printWidth( Box box ); // 注意这个声明为友元的函数并不是Box的成员函数
void setWidth( double wid );
};
// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
/* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
cout << "Width of box : " << box.width <<endl;
}
23.volatile
24.Lambda表达式
Lambda 函数可以引用在它之外声明的变量. 这些变量的集合叫做一个闭包. 闭包被定义在 Lambda 表达式声明中的方括
号 [] 内。这个机制允许这些变量被按值或按引用捕获
[] 未定义,使用任何外部变量都是错误的(编译错误)
[&] 按引用捕获外部变量
[=] 按值捕获外部变量
[x, &y] x按值捕获,y按引用捕获
[=, &z] z按引用捕获,其他按值捕获
void test5()
{
int x = 1;
//auto func1 = []() {cout << x << endl; }; //!< 编译错误,空白表示不使用任何参数,仅可使用形参列表中的参数
auto func2 = [&]() {cout << x << endl; };
auto func3 = [=]() {cout << x << endl; };
//auto func4 = [=]() { x = 3; }; //!< 编译错误,按值传递无法修改
auto func5 = [=]()mutable { x = 3; }; //!< 按值传递使用mutable可以修改,但只是修改副本
auto func6 = [&]() { x = 3; };
x = 2;
func2(); //!< 输出 2,按引用值可以修改
func3(); //!< 输出 1,按值传递,在穿件func3时值已经确定(副本)
func5();
cout << x << endl; //!< 输出 2,按值传递func5修改的是x的副本
func6();
cout << x << endl; //!< 按引用传递,值被改变
}