Basic
智能指针 Smart Pointers
void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars");
// Use pSong...
// Don't forget to delete!
delete pSong;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));
// Use song2...
wstring s = song2->duration_;
//...
} // song2 is deleted automatically here.
头文件
- Note that quotes are used for header files in the same directory as the source file, and angle brackets are used for standard library headers.
- Don’t put using statements in your header files!
- Many standard library headers do not have .h or any other file extension.
VS头文件路径主要分为两类目录
- C++安装目录:安装路径\Microsoft Visual Studio 版本号\VC, $(VC_IncludePath), 标准C++头文件目录和afxmfc相关头文件目录, C++安装目录\include 与 C++安装目录\afxmfc\include
- Windows SDK目录:C:\Program Files (x86)\Windows Kits\, $(WindowsSDK_IncludePath), Windows SDK头文件路径, Windows SDK目录\版本号\Include\版本号\ucrt、Windows SDK目录\版本号\Include\um、Windows SDK目录\版本号\Include\shared
继承类型
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
using
使用名称空间
using namespace std; ## 使用别名,using 类型别名=原类型
using uint=unsigned int;
uint i=0; ## 当一个派生类私有继承基类时,基类的public和protected数据成员在派生类中是private的形式,如果想让这些继承而来的数据成员作为public或者protected成员,可以用using来重新声明。using声明语句中名字的访问权限由该using声明语句之前的访问说明符决定。
class Basic{
public:
int a;
int b;
};
class Bulk : private Basic{
public:
using Basic::a;
protected:
using Basic::b;
}; ## 因为派生类可以重载继承自基类的成员函数,所以如果派生类希望所有的重载版本对于它都是可见的,那么它就要覆盖所有版本或者一个也不覆盖。但是,有时一个类仅需要覆盖重载部分函数,若覆盖所有函数,就太繁琐了。那么此时,using就派上用场了。只要为重载的成员函数提供一条using声明,这样我们就无需覆盖基类中的每一个版本了。
class Basic{
void func(){
cout<<""func()1"<<endl;
}
void func(int a){
cout<<"func()2"<<endl;
}
};
class Bulk : public Basic{
using Basic::func;
};
类访问修饰符
默认为private, 不管是成员还是函数。
将一个程序编译成可执行程序需要经历的四个步骤
- 预处理:去掉注释,进行宏替换(#define相关),头文件(#include)
- 编译:不同平台选用的汇编语言是不一样的。编译将高级语言编译成汇编语言
- 汇编:将汇编语言翻译成二进制的目标代码。
- 链接:包含各函数库的入口,得到可执行的代码。
静态库
之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。
- 静态库对函数库的链接是放在编译时期完成的。
- 程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
动态库
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
- 动态库把对一些库函数的链接载入推迟到程序运行的时期。
- 可以实现进程之间的资源共享。(因此动态库也称为共享库) 将一些程序升级变得简单。
- 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。
NULL & nullptr
NULL=0, nullptr代表空指针
GUID
需要在Linker -> Input -> Additional Dependencies中添加“Rpcrt4.lib”。 GUID guid; RPC_STATUS RpcStatus; RpcStatus = ::UuidCreate(&guid); RpcStatus = ::UuidCreateNil(&guid); int result = ::UuidIsNil(&guid, &RpcStatus);
Time
FILETIME
1601年1月1日的子夜开始的计数,FILETIME是以100纳秒为单位的。如果需要转化到1970年1月1日子夜开始的时间戳,需要减去0x19DB1DED53E8000。
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME;
#define DIFFERENCE_1601_1970 0x19DB1DED53E8000
COleDateTime
COleDateTime dateOne(1995, 3, 15, 12, 0, 0);
wcout << dateOne.Format(_T("%Y-%m-%dT%H:%M:%S")).GetString() << endl;
reinterpret_cast
强制类型转换
ccSemaphore* sema = reinterpret_cast<ccSemaphore * >(arg);
虚函数 virtual
c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此,在子类重新声明该虚函数时,可以加,也可以不加,但习惯上每一层声明函数时都加virtual,使程序更加清晰。
const
左定值,右定向,const修饰不变量。
//左定值, const 修饰指针指向的内容,则内容为不可变量。
const int *p = 8; //指针指向的内容 8 不可改变。简称左定值,因为 const 位于 * 号的左边。
//右定向, const 修饰指针,则指针为不可变量。
int a = 8;
int* const p = &a; //对于 const 指针 p 其指向的内存地址不能够被改变,但其内容可以改变。简称,右定向。因为 const 位于 * 号的右边。
//const 修饰指针和指针指向的内容,则指针和指针指向的内容都为不可变量。
int a = 8;
const int * const p = &a; //const p 的指向的内容和指向的内存地址都已固定,不可改变。
const参数传递和函数返回值
- 值传递的 const 修饰传递,一般这种情况不需要 const 修饰,因为函数会自动产生临时变量复制实参值。
- 当 const 参数为指针时,可以防止指针被意外篡改。
inheritance
类在继承时,需要指定继承方式,继承方式有三种:public>protectd>private
//class 新类的名字:继承方式 继承类的名字{};
class human {
public:
string name = "小明";
int age = 18;
};
class student:public human {
public:
int schoolnum = 666;
void print()
{
cout << name << endl << age << endl << schoolnum << endl;
}
};
参数批注
参数批注用于说明函数参数的性质和类型,可以帮助开发人员更好地了解如何使用这些参数。在VS 2019以前,并不要求在函数声明和定义中设置参数批注,参数批注仅用于指导程序员正确使用函数参数。为了正规一些,我们列出参数批注更好一些,以帮助大家正确使用函数参数。opt表示可选择(optional),表示可以不使用该参数,也可以设置为0或者NULL(0),4个不带opt的参数批注表示该参数必须指定一个合理的值,也就是说,你必须得填。
int APIENTRY WinMain(_In_ HINSTANCE hinstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
- In & _In_opt
- Inout & Inout_opt
- Out & Out_opt
- Outptr & Outptr_opt
多态
- 编译时多态 - 通过重载实现
- 运行时多态 - 通过虚函数实现,父类的函数用virtual修饰和子类要重写父类的函数
=delete
用于明确禁用或删除类的成员函数、特殊成员函数、或者其他成员函数。=delete的主要目的是在编译时捕获潜在的错误,并提供更精确的控制,以确保类的行为符合设计要求。使用=delete可以禁用类的默认构造函数、复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符或析构函数。这对于防止特定的操作非常有用,例如禁止对象的复制或禁止析构函数的调用。
class NonCopyable {
public:
NonCopyable() = default;
// 使用=delete禁止复制构造函数和复制赋值运算符
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
= default
=default 是C++11引入的一种特性,它允许显式要求编译器生成默认的特殊成员函数。特殊成员函数包括默认构造函数、复制构造函数、移动构造函数、复制赋值运算符、移动赋值运算符以及析构函数。在默认情况下,如果你没有显式提供这些特殊成员函数的定义,C++编译器会自动生成它们。但是,有时你可能需要明确告诉编译器生成这些函数,这通常在以下情况下很有用:
- 如果你手动提供了一个类的某个特殊成员函数的定义,但又希望编译器生成其他特殊成员函数,你可以使用=default 来请求编译器生成它们。
-
在某些情况下,如果你删除了某个特殊成员函数的默认定义(例如,删除了默认构造函数),但后来又需要该函数,可以使用=default 重新启用它。
#include
class MyClass { public: // 默认构造函数被指定为=default,编译器将生成它 MyClass() = default;
// 自定义构造函数 MyClass(int value) : value(value) {} void printValue() { std::cout << "Value: " << value << std::endl; }
private: int value = 0; };
int main() { MyClass obj1; // 使用生成的默认构造函数 MyClass obj2(42); // 使用自定义构造函数
obj1.printValue(); // 输出: Value: 0 obj2.printValue(); // 输出: Value: 42 return 0; }