C++

C++集锦

C++百问百答

Posted by Bob on December 19, 2018

版本

C++98(1.0)

C++03(TR1,Technical Report1)

C++11(2.0)

C++14

C++17

C++20

编译器及工具

C++ Compilers Wiki :

1.GNU Compiler Collection

2.Clang

3.Visual Studio

4.MinGW

5.Cygwin

6.Cmake

7.Scons

8.emake

9.fastbuild

资料

https://github.com/jobbole/awesome-cpp-cn

https://learn.microsoft.com/en-us/cpp/cpp/cpp-language-reference?view=msvc-170

const用法

const 修饰成员变量

int money = 18;

int const bank = money;

int *card = &money;

//指针所指数据是常量

const int *wechat = &money;

//指针本身是常量

int * const aiplay = &money;

//指针和指针所指数据都不能修改

const int * const bcoin = &money;

const修饰函数参数

void getMoney(const int value) {

    value = 5;//编译失败

}

const修饰成员函数


class Service {

	int money;

	void getMoney(int value) const {

		money = value;//编译失败

	}
};


const修饰函数返回值

///const data,non-const pointer

const int * getMoney(){

	int *money = new int();

	return money;

}

const int *card = getMoney();

变参的函数


#include "stdafx.h"
#include <stdio.h> 
#include <stdlib.h>
#include<stdarg.h>

void format(const char *p_format,...) {
	va_list argp;
	va_start(argp, p_format);
	const unsigned int BUFFER_SIZE = 16384;
	char buf[BUFFER_SIZE + 1];
	vsnprintf(buf, BUFFER_SIZE, p_format, argp);
	printf("%s",buf);
	va_end(argp);
};


int main()
{
	format("you are %s / %s","boy","girl");
	system("Pause");
	return 0;
}

传参数

传值 by value

//调用这个函数时先额外申请两个内存单元,存形参a和b的值1和2

void swap(int a, int b) {
	int temp = a;
	a = b;
	b = temp;
}

int main()
{
	int m = 1;
	int n = 2;
	swap(m, n);
	system("Pause");
	return 0;
}

传址

//调用这个函数时先额外申请两个内存单元,存形参a和b的地址

void swap(int *a, int *b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}

int main()
{
	int m = 1;
	int n = 2;
	swap(&m, &n);
	system("Pause");
	return 0;
}

引用 by reference

//函数时并不重新分配内存空间,形参和实参相同

void swap(int &a, int &b) {
	int temp = a;
	a = b;
	b = temp;
}

int main()
{
	int m = 1;
	int n = 2;
	swap(m, n);
	system("Pause");
	return 0;
}


虚函数

虚函数,在父类必须实现该函数; 纯虚函数,在父类中不需要实现该函数,子类必须实现。


#include <iostream>

using namespace std;

class BaseView {
public:
	BaseView() {  }
	virtual void Paint() { cout << "Paint BaseView" << endl; };
	virtual void Print() = 0;
};

class Button:BaseView {
public:
	Button() { Paint(); }
	virtual void Paint() { cout << "Paint Button" << endl; };
	virtual void Print() { cout << "Print Button" << endl; };
};


int main()
{
	Button btn;
	btn.Paint();
	system("Pause");
	return 0;
}

//结果: Paint BaseView  Paint Button Paint Button

explicit构造函数


#include <iostream>

using namespace std;

class BaseView {
public:
	int id = 0;
	explicit BaseView(int _id) { id = _id; }
};

int main()
{
	BaseView bv1(10);
	BaseView bv2 = BaseView(10);
	BaseView bv3 = 10; //编译错误  不允许隐式的转换

	system("Pause");
	return 0;
}

继承


#include <iostream>

using namespace std;

class BaseView {
public:
	BaseView() { cout << "BaseView()" << endl; };
	~BaseView() { cout << "~BaseView()" << endl; };
};

class Button:public BaseView {
public:
	Button() { cout << "Button()" << endl; };
	~Button() { cout << "~Button()" << endl; };
};


int main()
{
	Button* btn = new Button();
	delete btn;
	btn = 0;


	/*

	输出:
	
	BaseView()
	Button()
	~Button()
	~BaseView()

	*/

	system("Pause");
	return 0;
}

重载

成员函数被重载的特征:

1.相同的范围(在同一个类中); 2.函数名字相同; 3.参数不同; 4.virtual 关键字可有可无。


#include <iostream>

using namespace std;

class BaseView {
public:
	BaseView() {  }
	
};

class Button:BaseView {
public:
	Button() {  }
	void init() {};
	void init(int width, int height) {};
	void init(int width, int height,int length) {};
};


int main()
{
	Button btn;
	system("Pause");
	return 0;
}

覆盖

覆盖是指子类函数覆盖父类函数,特征是:

1.不同的范围(分别位于子类与父类); 2.函数名字相同; 3.参数相同; 4.基类函数必须有virtual 关键字。



#include <iostream>

using namespace std;

class BaseView {
public:
	BaseView() {  }
	virtual void init(int width, int height) { cout << "BaseView.init(int width, int height)" << endl; };
};

class Button:BaseView {
public:
	Button() {  }
	void init(int width, int height) { cout << "Button.init(int width, int height)" << endl; };
	
};


int main()
{
	Button btn;
	btn.init(10,20);
	system("Pause");
	return 0;
}

//结果:Button.init(int width, int height)

隐藏

如果子类的函数与父类的函数同名,并且参数也相同,但是父类函数没有virtual关键字。此时,父类的函数被隐藏(注意别与覆盖混淆)。



#include <iostream>

using namespace std;

class BaseView {
public:
	BaseView() {  }
	void init(int width, int height) { cout << "BaseView.init(int width, int height)" << endl; };
};

class Button:BaseView {
public:
	Button() {  }
	void init(int width, int height) { cout << "Button.init(int width, int height)" << endl; };
	
};


int main()
{
	Button btn;
	btn.init(10,20);
	system("Pause");
	return 0;
}

//结果:Button.init(int width, int height)

隐藏

如果子类的函数与父类的函数同名,但是参数不同。此时,不论有无virtual关键字,父类的函数将被隐藏(注意别与重载混淆)


#include <iostream>

using namespace std;

class BaseView {
public:
	void init(int width, int height) { cout << "BaseView.init(int width, int height)" << endl; };
	virtual void resetSize(int width, int height) { cout << "BaseView.resetSize(int width, int height)" << endl; };
	virtual void resetPos(int x, int y) { cout << "BaseView.resetPos(int x, int y)" << endl; };
};

class Button:public BaseView {
public:
	void init(float width, float height) { cout << "Button.init(float width, float height)" << endl; };
	virtual void resetSize(float width, float height) { cout << "Button.resetSize(float width, float height)" << endl; };
	virtual void resetPos(int x, int y) { cout << "Button.resetPos(int x, int y)" << endl; };
};


int main()
{
	Button btn;
	btn.init(10, 20);
	btn.resetSize(10, 20);
	btn.resetPos(10,20);



	Button btn2;
	BaseView *bptr1 = &btn2;
	Button *bptr2 = &btn2;

	//bad
	bptr1->init(10, 20);
	bptr2->init(10,20);

	//bad
	bptr1->resetSize(10, 20);
	bptr2->resetSize(10, 20);

	//good
	bptr1->resetPos(10, 20);
	bptr2->resetPos(10, 20);


	/*

	结果:
	Button.init(float width, float height)
	Button.resetSize(float width, float height)
	Button.resetPos(int x, int y)

	BaseView.init(int width, int height)
	Button.init(float width, float height)

	BaseView.resetSize(int width, int height)
	Button.resetSize(float width, float height)

	Button.resetPos(int x, int y)
	Button.resetPos(int x, int y)

	*/

	system("Pause");
	return 0;
}

static_cast

static_cast主要用于在相关类型之间进行转换,这些类型通常是有一定关联的,比如基本数据类型之间的转换(如int和float)、类层次结构中的向上转换(将派生类指针或引用转换为基类指针或引用)。它是一种编译时的类型转换操作,编译器会在编译阶段检查转换是否合理。

int i = 5;
float f = static_cast<float>(i);

class Base {
public:
    virtual void func() {}
};
class Derived : public Base {
public:
    void func() override {}
};
int main() {
    Derived d;
    Base* b = static_cast<Base*>(&d);
    return 0;
}

dynamic_cast

dynamic_cast主要用于在类的继承层次结构中进行安全的向下转换(将基类指针或引用转换为派生类指针或引用)或者交叉转换(在有多个继承分支的情况下)。与static_cast不同的是,dynamic_cast会在运行时检查转换是否有效,如果转换无效(比如基类指针实际指向的对象不是目标派生类的对象),对于指针类型的转换,它会返回nullptr;对于引用类型的转换,它会抛出std::bad_cast异常。

int main() {
    Base* b = new Derived;
    Derived* d = dynamic_cast<Derived*>(b);
    if (d) {
        // 转换成功后的操作
        d->func();
    }
    else {
        // 转换失败后的操作
        // 这里可能是b实际指向的不是Derived对象
    }
    delete b;
    return 0;
}

int main() {
    Base& b_ref = *new Derived;
    try {
        Derived& d_ref = dynamic_cast<Derived&>(b_ref);
        d_ref.func();
    }
    catch (std::bad_cast& e) {
        // 处理异常,当转换失败时会进入这里
    }
    return 0;
}

reinterpret_cast

reinterpret_cast是一种比较危险的类型转换操作,它可以将一种类型的指针转换为另一种完全不同类型的指针,或者将一个整数转换为指针,或者将指针转换为整数等。它只是简单地对二进制数据进行重新解释,不考虑类型之间的语义关系。这种转换通常用于底层的、和硬件或系统接口相关的编程场景,如直接操作内存地址等,但很容易导致程序出现错误。

int main() {
    int i = 10;
    // 将int*转换为char*
    char* c = reinterpret_cast<char*>(&i);
    // 这种操作可能会破坏类型的安全性
    // 这里只是简单地将int类型的内存表示按照char类型来解释
    return 0;
}

const_cast

修改const限定符示例 有时候,我们可能会遇到这样的情况,比如有一个函数接收一个const参数,但在函数内部其实需要对这个参数进行一些非const的操作。这可能是因为这个函数被设计得不够合理,或者是因为在某些特定的情况下(比如对一个原本不是const的对象进行了const引用传递,但在函数内部又需要修改它),需要去除const属性来进行操作

void updateCounter(const int* const pCounter) {
    // 这里通过const_cast去掉指针和指针所指对象的const属性
    int* nonConstCounter = const_cast<int*>(pCounter);
    (*nonConstCounter)++;
}
int main() {
    int counter = 0;
    updateCounter(&counter);
    return 0;
}

修改volatile限定符示例 volatile关键字用于告诉编译器,变量的值可能会在程序的控制流之外被改变,比如被硬件或者其他异步线程修改。const_cast也可以用于修改volatile限定符。

volatile const int* pReg = getHardwareRegisterAddress();
int* nonVolatileReg = const_cast<int*>(pReg);
// 进行一些非volatile的操作,例如在特定的初始化阶段设置寄存器的初始值
*nonVolatileReg = 0;

enum class

在 C++ 中,enum class(枚举类)是一种强类型的枚举。它解决了传统枚举(enum)的一些问题,比如枚举值的作用域问题和类型安全性问题。

enum class Color {
    RED,
    GREEN,
    BLUE
};
Color c = Color::RED;
// int i = c; // 这是错误的,不能隐式转换
int i = static_cast<int>(c); // 正确,需要显式转换

enum OldColor {
    RED,
    GREEN,
    BLUE
};
OldColor c = OLD_COLOR::RED;
int i = c; // 可以隐式转换为整数

枚举类中的枚举值的名字是在枚举类的作用域内的,这避免了名字冲突。

enum Fruit {
    APPLE,
    BANANA,
    ORANGE
};
enum Vegetable {
    CARROT,
    BROCCOLI,
    APPLE // 这里会产生命名冲突,因为APPLE已经在Fruit枚举中定义过了
};

enum class AnotherFruit {
    APPLE,
    BANANA,
    ORANGE
};
enum class AnotherVegetable {
    CARROT,
    BROCCOLI,
    APPLE // 不会产生命名冲突,因为作用域不同
};

Switch中使用

Color c = Color::GREEN;
switch (c) {
case Color::RED:
    std::cout << "It's red." << std::endl;
    break;
case Color::GREEN:
    std::cout << "It's green." << std::endl;
    break;
case Color::BLUE:
    std::cout << "It's blue." << std::endl;
    break;
}

类型精准

enum OldEnum {
    VALUE1 = 1,
    VALUE2 = 2,
    VALUE3 = 3
};

enum class NewEnum : unsigned char {
    VALUE1 = 1,
    VALUE2 = 2,
    VALUE3 = 3
};