C++通用学习速成判定
C++进制转换
常用进制转换方法(万能模板,bitset,strtol,stoi,itoa,std::dec, std::hex, std::oct)_CS生的博客-CSDN博客
std::cout << std::oct << 42; //52
std::cout << std::dec << 42; //42
std::cout << std::hex << 42; //2a
容器
#include <iostream>
#include <vector>
using namespace std;
void assign_demo()
{
vector<char> characters;
vector<char> characters2;
characters.assign(5, 'a');
characters2.assign(characters.begin(),characters.begin()+2);
for (char c : characters) {
cout << c << '\n';
}
for (char c : characters2)
{
cout << c << endl;
}
initializer_list<char> ilist = {'c','d'};
characters.assign(ilist);
for (char c : characters) {
cout << c << '\n';
}
}
int main(){
vector_demo();
return 0;
}
输出:
a
a
a
a
a
a
a
c
d
push_back
vector<int> v;
for (int i=0; i < 20; i++) {
v.push_back(i);
cout<<"i = "<<i
<< " size = " << v.size()
<< " capacity = " << v.capacity() << endl;
}
std::to_string(ii)
Cpu.push_back(new Processor("cpu_" + std::to_string(ii),
NUM_THREADS, Processor::SDV));
c.back
返回最后一个元素(不会检查最后一个元素是否存在)
std::vector使用总结_大山喵的博客-CSDN博客
C++初始化
C++(11):花括号列表初始化_c++ 花括号初始化_风静如云的博客-CSDN博客
第九行
#include <vector>
using namespace std;
int main(){
int i1 = 1; //等号赋值表达式初始化
int i2(2); //类似构造的圆括号表达式
int i3 = (3); //等号赋值表达式初始化
int i4{4}; //列表初始化
int i5 = {5}; //列表初始化
int a[]{1, 2, 3}; //列表初始化
int b[] = {4, 5, 6}; //列表初始化
vector<int> d{7, 8, 9}; //列表初始化
vector<int> e = {10, 11, 12}; //列表初始化
return 0;
}
C++的结构体的构造和析构
C++中struct也有构造函数与析构函数,也可以有访问类型控制,可以用private关键字。struct与class是小异大同。struct默认访问权限是public,class是private;class有继承,多态机制,而struct没有。
#include <iostream>
struct point
{
public:
point():x_(0.0),y_(0.0)
{
std::cout<<"default constructor point\n";
}
point(double x,double y):x_(x),y_(y)
{
std::cout<<"constructor point("<<x<<", "<<y<<")\n";
}
~point()
{
std::cout<<"default destructor point\n";
}
double get_x()
{
return x_;
}
double get_y()
{
return y_;
}
private:
double x_;
double y_;
};
class point_class
{
public:
point_class():x_(0.0),y_(0.0)
{
std::cout<<"default constructor point_class\n";
}
point_class(double x,double y):x_(x),y_(y)
{
std::cout<<"constructor point_class("<<x<<", "<<y<<")\n";
}
~point_class()
{
std::cout<<"default destructor point_class\n";
}
double get_x()
{
return x_;
}
double get_y()
{
return y_;
}
private:
double x_;
double y_;
};
int main()
{
point pt;
std::cout << pt.get_x() << "\n";
std::cout << pt.get_y() << "\n";
std::cout << "sizeof( double ): " << sizeof( double ) <<
", sizefof( point ): " << sizeof( point ) << "\n";
point_class pt_c;
std::cout << "sizeof( double ): " << sizeof( double ) <<
", sizefof( point_class ): " << sizeof( point_class ) << "\n";
}
C++的构造函数不能为虚函数
析构函数应当说明为虚函数,以实现调用。
C++基类的析构函数为何要声明为虚函数
#include <iostream>
using namespace std;
class base {
public:
base() {
cout << "base constructor" << endl;
int *b = new int[5];
}
~base() {
cout << "base destructor" << endl;
delete[] b;
}
private:
int *b;
};
class derived : public base {
public:
derived() {
cout << "derived constructor" << endl;
int *d = new int[8];
}
~derived() {
cout << "derived destructor" << endl;
delete[] d;
}
private:
int *d;
};
int main()
{
base *pBase = new derived;
cout << "---" << endl;
delete pBase;
return 0;
}
运行结果:
base constructor
derived constructor
---
base destructor
- 首先,基类的构造函数被调用(base constructor);
- 其次,派生类的构造函数也被调用(derived constructor);
- 最后,基类的析构函数被调用(base destructor)。
没有调用派生类的析构函数,这样会导致d指针所指向的整型存储空间不会被释放,从而造成内存泄漏。
virtual ~base() {
cout << "base destructor" << endl;
delete[] b;
}
将基类的析构函数声明为虚函数之后,派生类的析构函数也自动成为虚析构函数,在主函数中基类指针pBase指向的是派生类对象,当delete释放pBase指针所指向的存储空间时,
- 首先执行派生类的析构函数(derived destructor);
- 然后执行基类的析构函数(base destructor)。
base constructor
derived constructor
---
derived destructor
base destructor
C++构造函数和析构函数可以是虚函数吗
当类中声明虚函数时,编译器会在类中生成一个虚函数表,虚函数表是一个存储成员函数指针的数据结构。虚函数表是由编译器自动生成与维护的,virtual成员函数会被编译器放入虚函数表中,当存在虚函数时,每个对象都有一个指向虚函数的指针(vptr指针)。在实现多态的过程中,父类和派生类都有vptr指针。
vptr的初始化:当对象在创建时,由编译器对vptr指针进行初始化。在定义子类对象时,vptr先指向父类的虚函数表,在父类构造完成之后,子类的vptr才指向自己的虚函数表。如果构造函数时虚函数,那么调用构造函数就需要去找vptr,而此时vptr还没有初始化(vptr没有指向自己)。因此,构造函数不可以是虚函数。但是构造函数本身在类实例化的时候自动触发,不需要层次的调用结构。
C++的std::string和std::wstring
C++标准里 string和wstring_CodingAsura的博客-CSDN博客
前者string是常用类型,可以看作char[],其实这正是与string定义中的
_Elem=char相一致。而wstring,使用的是wchar_t类型,这是宽字符,用于满足非ASCII字符的要求,例如Unicode编码,中文,日文,韩文什么的。对于wchar_t类型,实际上C++中都用与char函数相对应的wchar_t的函数,因为他们都是从同一个模板类似于上面的方式定义的。因此也有wcout, wcin, werr等函数。 实际上string也可以使用中文,但是它将一个汉字写在2个char中。而如果将一个汉字看作一个单位wchar_t的话,那么在wstring中就只占用一个单元,其它的非英文文字和编码也是如此。这样才真正的满足字符串操作的要求,尤其是国际化等工作。
C++的shared_ptr
对应于垃圾回收机制的一种类模板实现。是堆内存空间的使用。make_shared用于初始化。如有可能,第一次创建内存资源时,请使用 make_shared 函数创建 shared_ptr。 make_shared 异常安全。 它使用同一调用为控制块和资源分配内存,这会减少构造开销。 如果不使用 make_shared,则必须先使用显式 new 表达式来创建对象,然后才能将其传递到 shared_ptr 构造函数。
std::shared_ptr<int> p1; //不传入任何实参
std::shared_ptr<int> p2(nullptr); //传入空指针 nullptr
std::shared_ptr<int> p3(new int(10));
std::shared_ptr<int> p3 = std::make_shared<int>(10);
//调用拷贝构造函数
std::shared_ptr<int> p4(p3);//或者 std::shared_ptr<int> p4 = p3;
//调用移动构造函数
std::shared_ptr<int> p5(std::move(p4)); //或者 std::shared_ptr<int> p5 = std::move(p4);
//自定义释放规则
void deleteInt(int*p) {
delete []p;
}
//初始化智能指针,并自定义释放规则
std::shared_ptr<int> p7(new int[10], deleteInt);
#include <iostream>
#include <memory>
using namespace std;
int main()
{
//构建 2 个智能指针
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2(p1);
//输出 p2 指向的数据
cout << *p2 << endl;
p1.reset();//引用计数减 1,p1为空指针
if (p1) {
cout << "p1 不为空" << endl;
}
else {
cout << "p1 为空" << endl;
}
//以上操作,并不会影响 p2
cout << *p2 << endl;
//判断当前和 p2 同指向的智能指针有多少个
cout << p2.use_count() << endl;
return 0;
}
10
p1 为空
10
1
C++的typeid和type_info
typeid和sizeof一样的地位,但是sizeof有时候可以不用括号。type_info用于存储typeid的返回值。
typeid( dataType )
typeid( expression )
#include <iostream>
#include <typeinfo>
using namespace std;
class Base{ };
struct STU{ };
int main(){
//获取一个普通变量的类型信息
int n = 100;
const type_info &nInfo = typeid(n);
cout<<nInfo.name()<<" | "<<nInfo.raw_name()<<" | "<<nInfo.hash_code()<<endl;
//获取一个字面量的类型信息
const type_info &dInfo = typeid(25.65);
cout<<dInfo.name()<<" | "<<dInfo.raw_name()<<" | "<<dInfo.hash_code()<<endl;
//获取一个对象的类型信息
Base obj;
const type_info &objInfo = typeid(obj);
cout<<objInfo.name()<<" | "<<objInfo.raw_name()<<" | "<<objInfo.hash_code()<<endl;
//获取一个类的类型信息
const type_info &baseInfo = typeid(Base);
cout<<baseInfo.name()<<" | "<<baseInfo.raw_name()<<" | "<<baseInfo.hash_code()<<endl;
//获取一个结构体的类型信息
const type_info &stuInfo = typeid(struct STU);
cout<<stuInfo.name()<<" | "<<stuInfo.raw_name()<<" | "<<stuInfo.hash_code()<<endl;
//获取一个普通类型的类型信息
const type_info &charInfo = typeid(char);
cout<<charInfo.name()<<" | "<<charInfo.raw_name()<<" | "<<charInfo.hash_code()<<endl;
//获取一个表达式的类型信息
const type_info &expInfo = typeid(20 * 45 / 4.5);
cout<<expInfo.name()<<" | "<<expInfo.raw_name()<<" | "<<expInfo.hash_code()<<endl;
return 0;
}
C++的继承
【C++】继承_c++继承_风继续吹TT的博客-CSDN博客
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
void Print()
{
cout << "_name _age _sex" << endl;
}
protected:
string _name;
int _age;
char _sex;
};
// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。
class Student :public Person
{
protected :
string _id;
};
int main()
{
Student s;
s.Print();
return 0;
}
C++的引用
如果不使用引用,而是使用*替代&,则在调用setValues函数后,需要使用 setValues赋值。
引用则是某块内存的别名。相当于解引用的p。
#include <iostream>
using namespace std;
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues(int i) {
double& ref = vals[i];
return ref; // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
}
// 要调用上面定义函数的主函数
int main ()
{
cout << "改变前的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素
cout << "改变后的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
return 0;
}
当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。
int& func() {
int q;
//! return q; // 在编译时发生错误
static int x;
return x; // 安全,x 在函数作用域外依然是有效的
}
C++ 把引用作为返回值 | 菜鸟教程
指针与引用的异同,以及如何相互转化_指针转引用_逢青丶的博客-CSDN博客
C++的模板
函数模板
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
类模板
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}
template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}
int main()
{
try {
Stack<int> intStack; // int 类型的栈
Stack<string> stringStack; // string 类型的栈
// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <<endl;
// 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
C++ 函数调用运算符 () 重载
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 重载函数调用运算符
Distance operator()(int a, int b, int c)
{
Distance D;
// 进行随机计算
D.feet = a + c + 10;
D.inches = b + c + 100 ;
return D;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main()
{
Distance D1(11, 10), D2;
cout << "First Distance : ";
D1.displayDistance();
D2 = D1(10, 10, 10); // invoke operator()
cout << "Second Distance :";
D2.displayDistance();
return 0;
}
C++ lambda表达式
C++11 lambda表达式精讲
lambda 表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。lambda 表达式的语法形式可简单归纳如下:
[ capture ] ( params ) opt -> ret { body; };
auto f = [](int a) -> int { return a + 1; };
std::cout << f(1) << std::endl; // 输出: 2
auto f = [](int a){ return a + 1; };
auto f1 = [](){ return 1; };
auto f2 = []{ return 1; }; // 省略空参数表
class A
{
public:
int i_ = 0;
void func(int x, int y)
{
auto x1 = []{ return i_; }; // error,没有捕获外部变量
auto x2 = [=]{ return i_ + x + y; }; // OK,捕获所有外部变量
auto x3 = [&]{ return i_ + x + y; }; // OK,捕获所有外部变量
auto x4 = [this]{ return i_; }; // OK,捕获this指针
auto x5 = [this]{ return i_ + x + y; }; // error,没有捕获x、y
auto x6 = [this, x, y]{ return i_ + x + y; }; // OK,捕获this指针、x、y
auto x7 = [this]{ return i_++; }; // OK,捕获this指针,并修改成员的值
}
};
int a = 0, b = 1;
auto f1 = []{ return a; }; // error,没有捕获外部变量
auto f2 = [&]{ return a++; }; // OK,捕获所有外部变量,并对a执行自加运算
auto f3 = [=]{ return a; }; // OK,捕获所有外部变量,并返回a
auto f4 = [=]{ return a++; }; // error,a是以复制方式捕获的,无法修改
auto f5 = [a]{ return a + b; }; // error,没有捕获变量b
auto f6 = [a, &b]{ return a + (b++); }; // OK,捕获a和b的引用,并对b做自加运算
auto f7 = [=, &b]{ return a + (b++); }; // OK,捕获所有外部变量和b的引用,并对b做自加运算
捕获列表
- [] 不捕获任何变量。
- [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
- [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
- [=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
- [bar] 按值捕获 bar 变量,同时不捕获其他变量。
- [this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。捕获 this 的目的是可以在 lamda 中使用当前类的成员函数和成员变量。
C++ 构造函数后加冒号
C++ 构造函数后加冒号_c++函数后面加冒号_mydriverc2的博客-CSDN博客
1、对含有对象成员的对象进行初始化, 例如,
类line有两个私有对象成员startpoint、endpoint,line的构造函数写成:
line(int sx,int sy,int ex,int ey):startpoint(sx,sy),endpoint(ex,ey){……}
初始化时按照类定义中对象成员的顺序分别调用各自对象的构造函数,再执行自己的构造函数
2、对于不含对象成员的对象,初始化时也可以套用上面的格式, 例如,<br /> 类rectangle有两个数据成员length、width,其构造函数写成:<br /> rectangle():length(1),width(2){}<br /> rectangle(int x,int y):length(x),width(y){}
3、对父类进行初始化, 例如,<br /> CDlgCalcDlg的父类是MFC类CDialog,其构造函数写为:<br /> CDlgCalcDlg(CWnd* pParent ): CDialog(CDlgCalcDlg::IDD, pParent)<br /> 其中IDD是一个枚举元素,标志对话框模板的ID<br /> 使用初始化成员列表对对象进行初始化,有时是必须的,有时是出于提高效率的考虑
C++ 中explicit的作用及用法
C++ 中explicit的作用及用法(虽然简单,但是还是有用的)_c++ explicit_夜猫程序猿的博客-CSDN博客
#include <bits/stdc++.h>
using namespace std;
class Circle
{
public:
Circle(){}
Circle(double _a):a(_a){}
Circle(int _b, int _c):b(_b), c(_c){}
Circle(const Circle& A)
{
a=A.a; b=A.b; c=A.c;
}
void Print()
{
cout<<a<<" "<<b<<" "<<c<<endl;
}
private:
double a;
int b;
int c;
};
class Circle1
{
public:
Circle1(){}
explicit Circle1(double _a):a(_a){}
explicit Circle1(int _b, int _c):b(_b), c(_c){}
explicit Circle1(const Circle1& A)
{
a=A.a; b=A.b; c=A.c;
}
void Print()
{
cout<<a<<" "<<b<<" "<<c<<endl;
}
private:
double a;
int b;
int c;
};
int main()
{
Circle q1(1);
Circle w1(2, 3);
q1.Print();
w1.Print();
//隐式调用, 不会报错
Circle q2 = 1;// just like Circle q(1); 调用的是Circle(double _a)
Circle w2 = 1.0;// just like Circle q(1.0); 调用的是Circle(double _a)
Circle e = q2;// 调用的是Circle(const Circle& A)
//隐式调用,会报错,注意Circle1()的构造函数,都有explicit
// Circle1 q3 = 1;
// Circle1 w3 = 1.0;
// Circle1 e1 = q3;
// 以上三行都会报错。
//显式调用,都不会有问题
Circle1 q3(1);
Circle1 w3(1.0);
Circle1 e1(q3);
}
C++ 仿函数
C++ 仿函数_c仿函数_恋喵大鲤鱼的博客-CSDN博客
本质上没有新的语法和功能,属于设计上的一种思路,将类伪装成函数,通过类的功能实现函数的可配置。
class StringAppend {
public:
explicit StringAppend(const string& str) : ss(str){}
void operator() (const string& str) const {
cout << str << ' ' << ss << endl;
}
private:
const string ss;
};
int main() {
StringAppend myFunctor2("and world!");
myFunctor2("Hello");
}
#include <iostream>
using namespace std;
class IsGreaterThanThresholdFunctor {
public:
explicit IsGreaterThanThresholdFunctor(int t):threshold(t){}
bool operator() (int num) const {
return num > threshold ? true : false;
}
private:
const int threshold;
};
int RecallFunc(int *start, int *end, IsGreaterThanThresholdFunctor myFunctor) {
int count = 0;
for (int *i = start; i != end + 1; i++) {
count = myFunctor(*i) ? count + 1 : count;
}
return count;
}
int main() {
int a[5] = {10,100,11,5,19};
int result = RecallFunc(a, a + 4, IsGreaterThanThresholdFunctor(10));
cout << result << endl;
}
C++的remove_if
std:;remove_if用法讲解_KFLING的博客-CSDN博客
remove_if的参数是迭代器,前两个参数表示迭代的起始位置和这个起始位置所对应的停止位置。
最后一个参数:传入一个回调函数,如果回调函数返回为真,则将当前所指向的参数移到尾部。remove_if在头文件algorithm中,故要使用此函数,需添加#include
由于remove_if函数的参数是迭代器,通过迭代器无法得到容器本身,而要删除容器内的元素必须通过容器的成员函数来进行。
因而此函数无法真正删除元素,只能把要删除的元素移到容器末尾并返回要被删除元素的迭代器,然后通过erase成员函数来真正删除。因为一般remove_if和erase函数是成对出现的。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
bool isSpace(char x) { return x == ' '; }
int main()
{
string s2("Text with spaces");
cout << "删除之前"<<s2 << endl;
s2.erase(remove_if(s2.begin(), s2.end(), isSpace), s2.end());
cout <<"删除之后"<< s2 << endl;
return 0;
}
删除之前Text with spaces
删除之后Textwithspaces
C++的bind1st bind2nd的使用
bind1st bind2nd的使用_simahao的博客-CSDN博客
int a[] = {1, 2, 100, 200};
std::vector< int> arr(a, a + 4);
// 移除所有小于100的元素
arr.erase( std::remove_if(
arr.begin(),
arr.end(),
std::bind2nd( std::less< int>(), 100)),
arr.end());
// 移除所有大于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
std::bind1st( std::less< int>(), 100)), arr.end());
// 移除所有大于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
std::bind2nd( std::greater< int>(), 100)), arr.end());
// 移除所有小于等于100的元素
arr.erase( std::remove_if( arr.begin(), arr.end(),
std::not1(std::bind2nd( std::greater< int>(), 100))), arr.end());
C++的for_each用法
c++ for_each 用法_小键233的博客-CSDN博客
void fun(int i )
{
cout<<i<<endl;
}
int main()
{
int a[] = { 1, 2, 3, 4};
vector<int> v(a, a+sizeof(a)/sizeof(int));
for_each(v.begin(), v.end(), fun);
}
void fun(int i, const char* str)
{
cout<<str<<i<<endl;
}
int main()
{
int a[] = { 1, 2, 3, 4};
vector<int> v(a, a+sizeof(a)/sizeof(int));
for_each(v.begin(), v.end(), bind2nd(ptr_fun(fun), "Element:"));
}
C++的bind用法
C++11中的std::bind 简单易懂_云飞扬_Dylan的博客-CSDN博客
double callableFunc (double x, double y) {return x/y;}
auto NewCallable = std::bind (callableFunc, std::placeholders::_1,2);
std::cout << NewCallable (10) << '\n';
bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。
因此std::bind(callableFunc,_1,2)等价于
std::bind (&callableFunc,_1,2);
_1表示占位符,位于<functional>中,std::placeholders::_1;
第一个参数被占位符占用,表示这个参数以调用时传入的参数为准,
在这里调用NewCallable时,给它传入了10,
其实就想到于调用callableFunc(10,2);
class Base
{
void display_sum(int a1, int a2)
{
std::cout << a1 + a2 << '\n';
}
int m_data = 30;
};
int main()
{
Base base;
auto newiFunc = std::bind(&Base::display_sum, &base, 100, std::placeholders::_1);
f(20); // should out put 120.
}
bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,
第二个参数表示对象的地址。
必须显式地指定&Base::diplay_sum,因为编译器不会将对象的成员函数
隐式转换成函数指针,所以必须在Base::display_sum前添加&;
使用对象成员函数的指针时,必须要知道该指针属于哪个对象,
因此第二个参数为对象的地址 &base;
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std::placeholders;
using namespace std;
ostream & printInfo(ostream &os, const string& s, char c)
{
os << s << c;
return os;
}
int main()
{
vector<string> words{"welcome", "to", "C++11"};
ostringstream os;
char c = ' ';
for_each(words.begin(), words.end(),
[&os, c](const string & s){os << s << c;} );
cout << os.str() << endl;
ostringstream os1;
// ostream不能拷贝,若希望传递给bind一个对象,
// 而不拷贝它,就必须使用标准库提供的ref函数
for_each(words.begin(), words.end(),
bind(printInfo, ref(os1), _1, c));
cout << os1.str() << endl;
}
class Produce : public action {
public:
output<Buf> out;
void body() {
out->x = 1;
std::cout << fullname() << " out=" << out->x << "\n";
}
};
class Transform : public action {
public:
input<Buf> in;
output<Buf> out;
void body() {
out->x = in->x + 1;
std::cout << fullname() << " in=" << in->x << " out=" << out->x << "\n";
}
};
class Consume : public action {
public:
input<Buf> in;
void body() {
std::cout << fullname() << " in=" << in->x << "\n";
}
};
C++的友元
C++ 友元函数 | 菜鸟教程
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend
#include <iostream>
using namespace std;
class Box
{
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};
// 成员函数定义
void Box::setWidth( double wid )
{
width = wid;
}
// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
/* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
cout << "Width of box : " << box.width <<endl;
}
// 程序的主函数
int main( )
{
Box box;
// 使用成员函数设置宽度
box.setWidth(10.0);
// 使用友元函数输出宽度
printWidth( box );
return 0;
}
C++的operator++()和operator++(int)
operator++()和operator++(int)的区别 - 莫水千流 - 博客园
class UPInt { // "unlimited precision int"
public:
UPInt& operator++(); // ++ 前缀
const UPInt operator++(int); // ++ 后缀
UPInt& operator--(); // -- 前缀
const UPInt operator--(int); // -- 后缀
UPInt& operator+=(int); // += 操作符,UPInts
// 与ints 相运算
...
};
UPInt i;
++i; // 调用 i.operator++();
i++; // 调用 i.operator++(0);
--i; // 调用 i.operator--();
i--; // 调用 i.operator--(0);
// 前缀形式:增加然后取回值
UPInt& UPInt::operator++()
{
*this += 1; // 增加
return *this; // 取回值
}
// postfix form: fetch and increment
const UPInt UPInt::operator++(int)
{
UPInt oldValue = *this; // 取回值
++(*this); // 增加
return oldValue; // 返回被取回的值
}
C++的operator->和智能指针
智能指针本身不是一个新的语法,而是一个概念。它用类作为实体,用操作符的重载,重载了++操作符,--操作符,->操作符,实现了智能指针的效果。
C++ 类成员访问运算符 -> 重载 | 菜鸟教程
#include <iostream>
#include <vector>
using namespace std;
// 假设一个实际的类
class Obj {
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
};
// 静态成员定义
int Obj::i = 10;
int Obj::j = 12;
// 为上面的类实现一个容器
class ObjContainer {
vector<Obj*> a;
public:
void add(Obj* obj)
{
a.push_back(obj); // 调用向量的标准方法
}
friend class SmartPointer;
};
// 实现智能指针,用于访问类 Obj 的成员
class SmartPointer {
ObjContainer oc;
int index;
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index = 0;
}
// 返回值表示列表结束
bool operator++() // 前缀版本
{
if(index >= oc.a.size() - 1) return false;
if(oc.a[++index] == 0) return false;
return true;
}
bool operator++(int) // 后缀版本
{
return operator++();
}
// 重载运算符 ->
Obj* operator->() const
{
if(!oc.a[index])
{
cout << "Zero value";
return (Obj*)0;
}
return oc.a[index];
}
};
int main() {
const int sz = 10;
Obj o[sz];
ObjContainer oc;
for(int i = 0; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc); // 创建一个迭代器
do {
sp->f(); // 智能指针调用
sp->g();
} while(sp++);
return 0;
}
在以上代码中,sp->f();本质上基本变为:oc.a[index].f()
C++的operator +重载与const
【C++】运算符重载关于const的分析(超详细)_c++ 运算符重载 const_萌宅鹿同学的博客-CSDN博客
class Point {
int m_x, m_y;
public:
Point(int x, int y) : m_x(x), m_y(y) {}
void display() {
cout << "(x1=" << m_x << ", x2=" << m_y << ")" << endl;
}
// 第一个const表示返回值是个常量,用来防止 (p1+p2)=Point(10,20); 这种赋值
// 括号内第二个const表示传入的参数可以接收const也可以接收非const,接收后的参数不能够在内部被变更
// 第三个const表示常量可以调用该函数,且定义的常量不能够被更改
// 具体见下面的分析。
const Point operator+(const Point &point) const {
return Point(m_x + point.m_x, m_y + point.m_y);
}
}
const Point p1 = Point(10, 20);
Point p2 = Point(20, 30);
Point p3 = p1 + p2; // 不加第3个const,这里会报错,因为p1是常数
// 注意,这里p1是常量,p2是变量,由于重载了“+”
// 上一句代码等同于 p1.operator+(p2);
// 由p1调用的operator+函数,因此调用该函数的是个常数。
// 同时p1这个常量不能够被更改。
C++的size_t
std::size_t的用法_size_t的范围_东风笑西风的博客-CSDN博客
在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的,这样利用该类型可以增强程序的可移植性。
std::size_t其实就是::size_t
C++的operator size_t
uint128_t 添加 c++ 重载类型强制转换 - 漆天初晓 - 博客园
operator uint64_t() { return lo; } //强制转换为uint64_t
operator uint32_t() { return (uint32_t)lo; }//强制转换为uint32_t
operator int32_t() { return (int32_t)lo; } //强制转换为int32_t
uint64_t a0 = (uint32_t)(this->lo);
uint64_t a1 = (uint32_t)(this->lo >> 0x20);
uint64_t a2 = (uint32_t)(this->hi);
uint64_t a3 = (uint32_t)(this->hi >> 0x20);
uint64_t b0 = (uint32_t)(b.lo);
uint64_t b1 = (uint32_t)(b.lo >> 0x20);
uint64_t b2 = (uint32_t)(b.hi);
uint64_t b3 = (uint32_t)(b.hi >> 0x20);
C++的typename
c++ 模板中 class T 和 typename T 的区别_Anadem的博客-CSDN博客
在模板声明中,typename 可用作 class 的代替品,以声明类型模板形参和模板形参 (C++17 起)。
在C++早期版本中,没有typename这个关键字,所以在模板定义的时候便使用了class。
因此现在使用typename更加合适。
C++的operator隐式类型转换
C++类的隐式类型转换运算符operator type()_你狗的博客-CSDN博客
对象向不同类的对象的转换
#include<iostream>
class X;
class A
{
public:
A(int num=0):dat(num) {}
A(const X& rhs):dat(rhs) {}
operator int() {return dat;}
private:
int dat;
};
class X
{
public:
X(int num=0):dat(num) {}
operator int() {return dat;}
operator A(){
A temp=dat;
return temp;
}
private:
int dat;
};
int main()
{
X stuff=37;
A more=0;
int hold;
hold=stuff;
std::cout<<hold<<std::endl;
more=stuff;
std::cout<<more<<std::endl;
return 0;
}
C的枚举与C++的类枚举
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum season {spring, summer=3, autumn, winter};
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
enum class Color {
Red,
Blue,
Black
};
int main(void)
{
Color color1 = Color::Blue; //正确
// Color color2 = 2; //错误,类型不匹配。编译报错
Color color3 = static_cast<Color>(10); //正确,但没意义。将整数进行强制类型转换,虽然语法没报错,但是值范围超出了枚举类成员值,没啥意义。
return 0;
}
C++的可变函数模板
#include <iostream>
using namespace std;
void print() {
cout << endl;
}
template <typename T> void print(const T& t) {
cout << t << endl;
}
template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
cout << first << ", ";
print(rest...); // recursive call using pack expansion syntax
}
int main()
{
print(); // calls first overload, outputting only a newline
print(1); // calls second overload
// these call the third overload, the variadic template,
// which uses recursion as needed.
print(10, 20);
print(100, 200, 300);
print("first", 2, "third", 3.14159);
}
C++的左值、右值、将亡值
简介C++11中的左值、纯右值、将亡值_纯右值和将亡值_执假以为真的博客-CSDN博客
左值:能够用&取地址的表达式是左值表达式。
函数名 (实际上是函数指针)
- 变量名(指的是是具名变量,如: std::cin、std::endl等)
- 返回左值引用的函数调用,如: std::getline(cin, str), cout << 1
- 前置运算表达式,如 ++i, --i
- 由赋值运算符或复合赋值运算符连接的表达式,如: a=b, a+=b,、a%=b
- 解引用表达式 *p
- 字符串字面值"abc"
- 具有名称的右值引用
右值:纯粹的字面值(如10,True),或者是 求值结果相当于字面值(除字符串)或不具名的临时对象。在C++11之前的右值和C++11中的纯右值是等价的。
- 字面值(除字符串字面值以外),如: 40, true, nullptr
- 返回值不是引用类型的函数调用,如: str.substr(1, 2), 2+2
- 后置运算符表达式,如 i++, i–
- 算术表达式,如: a+b, a&b, a<<b
- 逻辑表达式,如: a&&b, a||b, ~a
- 比较表达式,如: a==b, a>=b, a<b
- 取地址表达式,即 &a
- lambda表达式,如: [](int x)
- 转换目标为非引用类型的cast表达式
所谓的“将亡”,当一个右值准备完成初始化或赋值任务时,它就将亡了。
- 返回右值引用的函数的调用表达式
- 转换为右值引用的转换函数的调用表达式
以上二者返回的都是不具名的右值引用。
C++的auto关键字
https://zh.wikipedia.org/wiki/Auto_(C%2B%2B)
auto f=3.14; //double
auto s("hello"); //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型
std::vector<int> vect;
for(auto it = vect.begin(); it != vect.end(); ++it)
{ //it的类型是std::vector<int>::iterator
std::cin >> *it;
}
auto ptr = [](double x){return x*x;};//类型为std::function<double(double)>函数对象
在模板函数定义时,如果变量的类型依赖于模板参数,使用auto关键字使得在编译期确定这些类型,如:
template <class T, class U>void Multiply(T t, U u)
{
auto v = t * u;
std::cout<<v;
}
模板函数的返回类型如果也是依赖于从模板参数推导,
template <typename _Tx, typename _Ty>
auto multiply(_Tx v1, _Ty v2) -> decltype( _Tx * _Ty )
{
return v1*v2;
}
auto result = multiply(101, 1.414); // 结果类型是double
auto关键字可以从C++11风格的花括号{与}包围的值列表推导出std::initializer_list;而模板函数的形参推导时不认为这种值列表是一个类型,因此不能由值列表推导出std::initializer_list类型。
const int v1 = 101;
auto v2 = v1; // v2类型是int,脱去初始化表达式的顶层const
v2=102; // 可赋值
auto al = { 10, 11, 12 };//类型是std::initializer_list<int>
template<class T> void foo(T arg); // 函数模板声明
foo(v2); //函数模板实例化为 void foo<int>(int)
使用auto关键字声明变量的类型,不能自动推导出顶层的CV-qualifiers,也不能自动推导出引用类型,需要显式指定。
C++ cv-qualifier
C++ cv-qualifier - 腾讯云开发者社区-腾讯云
cv-qualifier 有三种:const and volatile, mutable。
如果需要具有顶层的CV-qualifiers,或者引用的类型,解决办法是显式指明:
const auto& v3=v1;
foo<const int&>(v1);//直接指明模板参数类型
template<class T> void foo(const T& arg);//或者偏特化模板函数
auto关键字还带上&号,声明引用类型,则不执行const剥除(如果不抑制const剥除,则得到了一个非常量引用型变量,指向了const变量,这显然是不可接受的。):
const int c = 0;
auto& rc = c;
rc = 44; // 编译错误,const int类型
C++的右值引用
C++11右值引用(一看即懂)
C++11 标准新引入了另一种引用方式,称为右值引用,用 "&&" 表示。需要注意的,和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化,比如:
int num = 10;
//int && a = num; //右值引用不能初始化为左值
int && a = 10;
和常量左值引用不同的是,右值引用还可以对右值进行修改。例如:
int && a = 10;
a = 100;
cout << a << endl;
const int&& a = 10;//编译器不会报错
C++的完美转发和引用折叠
C++11完美转发及实现方法详解
完美转发,它指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。
在没有右值引用的时候,实现完美转发如下:
#include <iostream>
using namespace std;
//重载被调用函数,查看完美转发的效果
void otherdef(int & t) {
cout << "lvalue\n";
}
void otherdef(const int & t) {
cout << "rvalue\n";
}
//重载函数模板,分别接收左值和右值
//接收右值参数
template <typename T>
void function(const T& t) {
otherdef(t);
}
//接收左值参数
template <typename T>
void function(T& t) {
otherdef(t);
}
int main()
{
function(5);//5 是右值
int x = 1;
function(x);//x 是左值
return 0;
}
C++11 标准中规定,通常情况下右值引用形式的参数只能接收右值,不能接收左值。但对于函数模板中使用右值引用语法定义的参数来说,它不再遵守这一规定,既可以接收右值,也可以接收左值(此时的右值引用又被称为“万能引用”)。在 C++11 标准中实现完美转发,只需要编写如下一个模板函数即可,此模板函数的参数 t 既可以接收左值,也可以接收右值:
template <typename T>
void function(T&& t) {
otherdef(t);
}
如果调用 function() 函数时为其传递一个左值引用或者右值引用的实参,如下所示:
然而,由 function(num) 实例化的函数底层就变成了 function(int && t),
同样由function(num2) 实例化的函数底层则变成了 function(int && && t)。
int n = 10;
int & num = n;
function(num); // T 为 int&
int && num2 = 11;
function(num2); // T 为 int &&
C++98/03 标准是不支持这种用法的,而 C++ 11标准为了更好地实现完美转发,特意为其指定了新的类型匹配规则,又称为引用折叠规则(假设用 A 表示实际传递参数的类型):
- 当实参为左值或者左值引用(A&)时,函数模板中 T&& 将转变为 A&(A& && = A&);
- 当实参为右值或者右值引用(A&&)时,函数模板中 T&& 将转变为 A&&(A&& && = A&&)。
函数模板内部来说,形参既有名称又能寻址,因此它都是左值。那么如何才能将函数模板接收到的形参连同其左、右值属性,一起传递给被调用的函数呢?C++11 标准的开发者已经帮我们想好的解决方案,该新标准还引入了一个模板函数 forword
#include <iostream>
using namespace std;
//重载被调用函数,查看完美转发的效果
void otherdef(int & t) {
cout << "lvalue\n";
}
void otherdef(const int & t) {
cout << "rvalue\n";
}
//实现完美转发的函数模板
template <typename T>
void function(T&& t) {
otherdef(forward<T>(t));
}
int main()
{
function(5);
int x = 1;
function(x);
return 0;
}
c++的类型转换
浅析c++中的类型转换--static_cast_一苇渡江694的博客-CSDN博客
谷歌编程规范指出,要使用c++的类型转换操作符,如static_cast。而坚决抵制c语言中的强制类型转换,例如int y = (int)x。
static_cast不够安全,就是指在运行阶段不进行类型检查
c++类型转换运算符有:
static_cast
dynamic_cast
const_cast
reinterpret_cast
typedef unsigned char BYTE;
void f() {
char ch;
int i = 65;
float f = 2.5;
double dbl;
ch = static_cast<char>(i); // int to char
dbl = static_cast<double>(f); // float to double
i = static_cast<BYTE>(ch);
}
// static_cast_Operator_2.cpp
// compile with: /LD /GR
class B {
public:
virtual void Test(){}
};
class D : public B {};
void f(B* pb) {
D* pd1 = dynamic_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}
C/C++ 的assert()
C/C++ 中assert()函数用法总结_askunix_hjh的博客-CSDN博客
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
int main( void )
{
FILE *fp;
fp = fopen( "test.txt", "w" ); //以可写的方式打开一个文件,如果不存在就创建一个同名文件
assert( fp ); //所以这里不会出错
fclose( fp );
fp = fopen( "noexitfile.txt", "r" ); //以只读的方式打开一个文件,如果不存在就打开文件失败
assert( fp ); //所以这里出错
fclose( fp ); //程序永远都执行不到这里来
return 0;
}
C++的字符串转换
"pi is " + std::to_string(3.1415926);
Le vent se lève! . . . il faut tenter de vivre!
Le vent se lève! . . . il faut tenter de vivre!