C++编程笔记

编程入门 行业动态 更新时间:2024-10-06 16:16:06

C++编程<a href=https://www.elefans.com/category/jswz/34/1770047.html style=笔记"/>

C++编程笔记

目录

  • C++编程小笔记1
    • 1.const的应用
    • 2.参数列表构造函数
    • 3.this指针
    • 4.friend 友元
    • 5.重载
    • 6.继承
      • 6.1处理同名元素
      • 6.1菱形继承
    • 7.多态
    • 8.文件读写
    • 9.模板
    • 10.异常机制
    • 11.标准输入输出
    • 12.回调函数
    • 13.移动构造
    • 14. 类的静态成员
    • 17.concept关键字
    • 18.using定义函数指针

C++编程小笔记1

1.const的应用

//栈区或静态存储区void printStu(const Student *p) {   //使用const修饰,使得传入参数只读//p->name = "老王";  锁定p,使其只读cout << p->name << endl;
}
void printStu(const Student &p) {cout<<p.name<<endl;
}void myConst(){int *const p1 = &a;//指针常量:指向不能改,值可以改*p1 = 100;const int *p2 = &b;//常量指针:值不能改,指向可改p2 = &a;}
class Stu{mutable int name;		//mutable修饰的关键字,在常函数和常对象中都可以修改void getName()const{	//常函数,在类中不允许修改指针的指向和值this->name="张三";	//常函数中,仅允许修改用mutable修饰的关键字}
}
const Stu S;			//常对象,仅能调用常函数//仿函数
class MyCompare {//仿函数,指能行使函数功能的类
public://后面的const表示函数不会修改任何成员数据的值bool operator()(const int v1,const int v2) const {return v1 > v2;}
};

2.参数列表构造函数

函数调用顺序:父构造,子构造,子析构,父析构

class Person {int m_A, m_B, m_C;Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c) {//初始化列表构造函数}
};

C++在定义对象时,如果同时用=对其进行赋值,就相当于自动调用了相应的构造函数

#include <iostream>
using namespace std;class my{
public:my(){cout<<"无参构造"<<endl;}my(const char * s){cout<<"string构造函数"<<endl;this->ss=s;}my(int a){cout<<"int构造函数"<<endl;this->aa=a;}my(const my &m){cout<<"拷贝构造"<<endl;this->ss=m.ss;this->aa=m.aa;}my(const my &&m){cout<<"移动构造"<<endl;this->ss=move(m.ss);this->aa=m.aa;}my&operator=(int a){cout<<"operator="<<endl;this->aa=a;}string ss;int aa;};
int main()
{my a="123";my b=1;my c;c=1;return 0;
}

运行结果:

string构造函数
int构造函数
无参构造
operator=
Hello World

3.this指针

与Java的this指针功能相似

Persons &addAge(const Persons& p) {			//传入只读变量引用this->age += p.age;return *this;						//返回当前对象}this是指针常量,不可修改  //this=NULL 是不合法的

4.friend 友元

友元函数 类内申明类外实现

class Buliding{
public:House H;void visitBedRoom(){cout<<H.bedRoom<<endl;//因为Builing是House的友元类,所以可以访问私有成员}
}
class House {friend void getHouse(const House &H);friend class Building;//友元类,可以访问隐私成员private:string bedRoom;
public:string bathRoom;};void getHouse(const House &H) {		//全局友元函数cout << H.bathRoom << endl << H.bedRoom << endl;
}

5.重载

//重载输出符class myInt {
public:myInt &operator++();myInt(int _a) : A(_a) {}friend ostream &operator<<(ostream &os, myInt &my);private:int A;
};ostream &operator<<(ostream &os, myInt &my) {os << my.A;return os;
}myInt &myInt::operator++() {this->A += 1;return *this;
}//重载
class MyStr{
public:int id;char *name;MyStr& operator =(const MyStr& str)//赋值运算符{cout << "operator =" << endl;if (this != &str)				//二者不相等{if (name != NULL){delete[] name;}this->id = str.id;int len = strlen(str.name);name = new char[len + 1];strcpy_s(name, strlen(str.name) + 1, str.name);}return *this;}
//通过函数重载运算符
Complex operator+(Complex &C) {                             Complex X(this->a + C.getA(), this->b + C.getB());return X;}T &operator[](int index) {return this->P[index];}

6.继承

父类中所有的非静态成员都会被继承成为子类的一部分,所以子类所占的大小位父类大小加上自身数据大小

6.1处理同名元素

    cout << "Son下的 X = " << S.X << endl;cout << "Base下的 X = " << S.Base::X << endl;//通过这种方式访问父类中的同名元素//父类成员函数调用也是如此//子类中访问父类同名成员
class Son : public Base {
public:Son() {X = 20;Base::X=30;}int X;
};
  • 静态成员也可以通过以上方式调用,还可以直接通过类名调用
  • 若出现同名成员函数,则子类函数会隐藏父类函数,调用父类函数则需要加作用域
    ( 子类名 ( 对象 ) :: 父类名 ( 对象 ) :: 函数名 )
  • 使用虚继承可以解决菱形继承的问题

6.1菱形继承

7.多态

多态为了写代码尽量遵循开闭原则,对修改关闭,对扩展开放,方便扩展和维护

class Animal {
public:virtual void Speak() {cout << "动物叫唤" << endl;}
};class Cat : public Animal {
public :void Speak() {cout << "喵喵喵喵喵" << endl;}
};class Dog : public Animal {
public:void Speak() {cout << "汪汪汪汪汪" << endl;}
};void DoSpeak(Animal &A) {A.Speak();
}//使用
Cat C;
DoSpeak(C);	//喵喵喵喵喵Animal *A = new Dog();
A->Speak();

使用父类指针指向子类对象时,delete该对象不会执行子类的析构函数,有可能造成内存泄漏
为了解决这个问题,需要使用虚析构函数,即在父类析构函数前加上virtual关键字

8.文件读写

srand((unsigned) time(NULL));//随机数种子
rand() % 10000 + 10000void test01() {ofstream ofs;string path = "/home/lhh/Project/C_C++/Test4_file/test.txt";ofs.open(path, ios::out);            //写文件ofs << "姓名:张三" << endl << "年龄:18" << endl;ofs.close();
}void test02() {ifstream ifs;string path = "/home/lhh/Project/C_C++/Test4_file/test.txt";ifs.open(path, ios::in);if (!ifs.is_open()) {cout << "文件无法打开" << endl;return;} else {cout << "打开成功" << endl;char buff1[1024], buff2[1024];//方法一
//        while (ifs >> buff1) {        //逐行读
//            cout << buff1 << endl;    当一行大于1024时就不行了
//        }cout << "---------------" << endl;//方法二
//        while (ifs.getline(buff2, sizeof(buff2))) {
//            cout << buff2 << endl;
//        }
//      方法三
//        string str;
//        while(getline(ifs,str)){
//            cout<<str<<endl;
//        }char c;while ((c = ifs.get()) != EOF) {//逐字节读取cout << c;}ifs.close();}
}

二进制文件读写写入和读取对象

//二进制写入
void test03() {ofstream ofs;ofs.open("/home/lhh/Project/C_C++/Test4_file/Person.txt", ios::out | ios::binary);Person p[2] = {{"张三", 18, "男"},{"李四", 20, "男"}};ofs.write((const char *) p, sizeof(Person));ofs.write((const char *) (p + 1), sizeof(Person));ofs.close();
}//二进制读取
void test04() {ifstream ifs;ifs.open("/home/lhh/Project/C_C++/Test4_file/Person.txt", ios::in | ios::binary);if (!ifs.is_open()) {cout << "读取失败" << endl;return;}Person *p = new Person;for (int i = 0; i < 2 && !ifs.eof(); ++i) {ifs.read((char *) p, sizeof(Person));cout << p->name << endl << p->age << endl << p->sex << endl;}delete p;//cout << (p+1)->name << endl << (p+1)->age << endl << (p+1)->sex << endl;ifs.close();
}
  • String转为char数组的方法
    string myStr = "dasffdas";char S[myStr.length()+1];memset(S,'\0',myStr.length()+1);strncpy(S, &myStr[0], sizeof(myStr));//目标地址  源地址  复制字符数量cout << S << endl;

使用流持续写入文件时,由于数据并不是直接进入文件,而是先放进缓冲区,大量写入时可能会导致缓冲区数据被覆盖导致数据丢失,所以需要flush保证数据被写入文件

#include<fstream>
int main(){std::ofstream outfile("test.txt");for(int n=0; n<100; ++n{outfile<<n;outfile.flush();}outfile.close();return 0;
}

9.模板

//函数模板写法
template<typename T>
void Swap(T &S1,T &S2){T temp = S1;S1 = S2;S2 = temp;
}
int main(){int a = 10,b = 20;float c = 1.1,d = 2.2;//隐式转换Swap(a , b);//显示声明Swap<float>(c , d);
}//类模板
template<class T1,class T2>
class Person{Person(T1 _name;T2 _age):name(_name),age(_age){//类内实现};T1 name;T2 age;void show();
}//构造函数类外实现
template<class T1,class T2>
Person<T1,T2>::Person(T1 _name,T2 _age):name(_name),age(_age){}//成员函数类外实现
template<class T1,class T2>
void Person<T1,T2>::show(){cout<<"name: "<<name<<'\t'<<"age: "<<age<<endl;
}

判定某个类型是否有特定的成员函数

template<typename T>
struct has_memberfunc_foo
{
private://这里重载两个Check,使得即使第一个无法匹配,也不会报错template<typename U>static auto Check(int) -> decltype( std::declval<U>().foo(), std::true_type() );template<typename U>static std::false_type Check(...);
public:enum { value = std::is_same<decltype(Check<T>(0)),std::true_type>::value  };
};
//判定是否有成员aa
template<typename T>
struct has_memberfunc_aa
{
private://这里重载两个Check,使得即使第一个无法匹配,也不会报错template<typename U>static auto Check(int) -> decltype( std::declval<U>().aa, std::true_type() );template<typename U>static std::false_type Check(...);
public:enum { value = std::is_same<decltype(Check<T>(0)),std::true_type>::value  };
};struct myStruct
{void foo() { std::cout << "hello" << std::endl;  
};
int main(){if( has_memberfunc_foo<myStruct>::value )//是否有成员函数foostd::cout << "myStruct has foo funciton"  << std::endl;
return 0;
}————————————————
版权声明:本文为CSDN博主「帝江VII」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:

使用decltype来获取成员函数的返回类型,如果获取成功则返回true

10.异常机制

float Div(int a, int b) {if (0 == b) {throw b;    //抛出一个异常,可以是任何数据或者对象}return a / b;
}void Div_use() {try {Div(10, 0);} catch (int e) {       	//接收异常,要用对应类型去接受,可以抛出多个不同类型的异常,//也可以接收多个不同类型的异常cout << "除数e = " << e << endl;} catch (...){				//捕获所有异常cout<<"未知类型异常!"<<endl;}
}//--------------------------------------------------------------------------void func() noexcept(false);//新写法,括号内写true就表示此函数不可//抛出异常,如果抛出,程序不会崩溃,而是自动终止void func() {//函数实现...
}

11.标准输入输出

知识来源

//cin.get()                 //读入一个字符并返回它的值
//cin.get(一个参数)  //读入一个字符并把它存储在ch 
//cin.get(两个参数)  //可以读取字符串
//cin.get(三个参数)  //可以读字符串        
//cin.getline()
//cin.ignore()           //读取字符并忽略指定字符
//cin.peek()            //检查下一个输入的字符,不会把字符从流中移除
//cin.putback()       //返回一个字符给一个流
//cin.clear()  //清空cin缓冲void test(){cout<<"输入字符或者数字"<<endl;char ch;cin.get(ch);//从缓冲区获取一个字符if(ch>='0' && ch<='9'){cin.putback(ch);int numget;cin>>number;cout<<"输入了数字"}else{char buf[256];cin>>buf;cout<<buf<<endl;}
}

12.回调函数

//定义函数指针
typedef int(*callback)(int);
//定义函数体
int pin(int x) {return x * x;
}
//实现回调
int getPinfang(int x, callback C) {return C(x);
}
//使用
void test() {getPinfang(10, pin);
}

13.移动构造

首先明确两个概念 左值右值

  • 左值:相当于一个盒子,代表内存空间
  • 右值:一个临时变量,一个值,带表的就是一个值
int a = 10;
a 就是左值,10 就是右值
class myClass{
int *memory = nullptr;			//当前类所占有的内存空间
myClass(myClass &&myclass){		//移动构造this->memory = myclass.memory;myclass.memory = nullptr;	//移动构造就是直接将你的东西取过来归我所用,然后在剥夺你的使用权}
};

14. 类的静态成员

静态成员变量必须在类中声明,在类外定义+初始化

class P{
public:static int i;
}
int P::i=100;

静态成员直接通过类名::变量名进行访问

cout<<"i = "<<P::i<<endl;
//赋值
P::i=20;

静态成员是所有类对象共享的

此外,还有静态成员函数

17.concept关键字

最简单的concept案例

template <typename T>
concept integral = std::is_integral_v<T>;
int a;
intergral<a>

判断传入的数据类型是否为int,是的话返回true
还有一种用法就是使用requires关键字

template<typename T>
concept printable=requires(T t){t.empty();
};
class A {
public:void empty() {}
};
class B {
public:
};cout << "A has " << printable<A> << std::endl;
cout << "B has " << printable<B> << std::endl;

运行结果

判断传入的类型能否执行requires内的内容,能正常执行则返回true这里就是判断类内是否又成员函数empty(),还可以写为

template<typename T>
concept printable=requires(T t){//是否有empty函数t.empty();//是否有to_string函数且函数返回值为string{t.to_string()}->std::same_as<string>;
};
class A {
public:void empty() {}
};
class B {
public:
};cout << "A has " << printable<A> << std::endl;
cout << "B has " << printable<B> << std::endl;

C++11中可以使用decltype+declval判断

template<class T>
struct has_val {
private:template<class U>static auto Check(int) -> decltype(std::declval<U>().val, std::true_type());//如果U有val,则编译通过返回true_typetemplate<class U>static std::false_type Check(...);
public:enum {value = std::is_same<decltype(Check<T>(0)), std::true_type>::value};
};
class T;
int main(void){if(has_val<T>::value){cout<<"有val成员变量";}else{cout<<"无val成员变量";}
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:
————————————————
版权声明:本文为CSDN博主「鸟哥01」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:

18.using定义函数指针

void S(int a,int b);
using i_i = void(*)(int,int);	//定义了一个函数指针,指向返回值为void,参数为int,int类型的函数
//
typedef (*i_i)(int,int);	//等价于上一个函数指针的定义i_i f1 = S;f1(1,2);					//调用

使用using关键字定义的函数指针,看着更加像一个赋值操作,便于理解


使用函数指针指向类的非静态重载成员函数


class GrilFriend : {std::string m_name;
public:explicit GrilFriend(std::string name);void hungry();void hungry(const string &str);
};
//这种方式可以直接区分两个重载的类成员函数
using hunger1 = void (GrilFriend::*)();
using hunger2 = void (GrilFriend::*)(const string &str);
hunger1 h1 = &GrilFriend::hungry;			//这样指向的就是hunger的无参版本
hunger2 h2 = &GrilFriend::hungry;

更多推荐

C++编程笔记

本文发布于:2024-02-14 12:02:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1763030.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:笔记

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!