C++学习笔记(B站黑马程序员C++教程)

C++学习笔记(B站黑马程序员C++教程),第1张

C++学习笔记(B站黑马程序员C++教程) 简介

视频链接https://www.bilibili.com/video/BV1kV411U7Ub?spm_id_from=333.999.0.0

建议用python或者浏览器视频插件把视频先下下来,万一没了呢!!!

哈哈哈,好久不见,说好的想把学过的一些东西整理成教程,一是方便大家学习和快速开发,

二是自己总结查漏补缺,帮助自己快速开发查找资料,但奈何鸽心太重,鸽一鸽就过了好久,也是

最近比较忙分身乏术吧。今天正好翻到以前的笔记,以前整理自己看的,先发出来供大家参考一下吧。

先整理发十章吧,里边会有一些自己的理解,不对的地方望大家指正,共同学习进步!

目录

一、初识C++

二、命名空间

三、 C++不能优化的

四、 内联函数

五、 参数缺省函数重载

六、 C++兼容C语言调用

七、构造和析构

八、构造函数的调用

九、深拷贝和浅拷贝的问题

十、类对象作为类成员

一、初识C++
面向对象三大特征:封装、继承、多态
命名空间:
    1.为什么有命名空间,是因为多人合作取标志符是重命名的问题
    2.什么是命名空间:
        using namespace std;
        //命名空间
        namespace MDZ {//MDZ命名空间
         int a;
         void func()
         { 
        
         }
        }
    3.命名空间注意:
     注意1:命名空间只能写在全局; 
     注意2:命名空间可以嵌套命名空间
      //命名空间可以嵌套命名空间
       namespace YDMDZ {
              int a;
         namespace LZMDZ {
              int b;
         }
      }
      注意3:命名空间是开放的,随时可以加入新成员,
        但是新成员只能在加入后使用       

作用域运算符:
 “::”
 通常情况下,如果有两个同名变量,另一个是局部变量,
 那么局部变量再起作用域内具有较高的优先权,他将屏蔽全局变量
二、命名空间
命名空间(重点)
4//类似于static int d = 50   不允许别的文件用
namespace
{
    int d = 50;
}
5//命名空间起别名
void test01()
{              //新名字    旧名字
    namespace nameMaker = Maker;
}
6分文件编写代码时,如果.h中有两个命名空间,但是里面的成员函数或成员变量同名时,在.cpp中实现这个函数时,
需要加上命名空间
using声明和编译指令(重点)
namespace A
{
    int a =10;
    int b =20;
    int c =30; 
}
void test01()
{
    //开放命名空间的某个元素,作用域为test01,且不可以重载变量
    //using声明是让命名空间中某个标识符可以直接使用
    using A::a; //只能用该命名空间的a
    cout << a < b ? a : b);
 //(a > b ? a : b)=100 error
 //这个表达式返回的是右值,是数值,返回的是20
 //20 = 100   xxx
 *(a > b ? &a : &b) = 100;
 printf("b=%d\n,b");
}
C++
void test03() {
 int a = 10;
 int b = 20;
 
 (a > b ? a : b) = 100;
 cout << "a=" << a << endl;
 cout << "b=" << b << endl;
 //c++的三目运算符返回的是左值,是空间
 *(a > b ? &a : &b) = 100;
 
}
//
在C++中可以放在赋值 *** 作的左边是左值,可以放到 *** 作符右面的是右值
有些变量既可以当左值,也可以当右值。
左值为 Lvalue,L代表Location,表示内存可寻址,可以赋值
右值为 Rvalur,Read,就是可以知道他的值
C/C++的const(重点)
C
1、c语言const修饰都有空间
2、C语言const修饰的全局变量具有外部链表属性
const int a=10;
void test()
{
    //a=200;全局的const不能直接修改
    // int *p=(int *)&a;
    //*p =100;//全局的const不能间接修改
}
void mian()
{
    const int b = 20;
    //b = 200;局部的const不能直接修改
    int *p=(int *)&b;
    //b = 200;局部的const可以间接接修改
    printf("%d",b);
}
C++
1.const 修饰的变量有时候有空间有时候没空间(没有空间是发生常量折叠,且没有变量进行取址 *** 作)
2.C++语言const修饰的全局变量没有外部链表属性
  只有内部文件属性。(保护本文件变量)
3.每个文件都使用extern int a; 可以在另一个文件中共用a;
const int aa=10;//没有内存没地址
void test()
{
    //常量折叠
    cout << "aa=" << aa <age = 10;
}
void test06() {
 Person* p = NULL;
 allocate_space(&p);

 cout << "p.age=" << p->age << endl;
}
//起别名方式同级操作
void allocate_space2(Person* &pp)
{
 pp= (Person*)malloc(sizeof(Person));
 pp->age = 20;
}
void test07() {
 Person* p = NULL;
 allocate_space2(p);

 cout << "p.age=" << p->age << endl;
}
string类型数据
可以直接赋值字符串
#include 
string name = "mdz";

四、内联函数
设计一个类求圆的周长
专业术语,实例化对象
class Circle {
public:
   //称为:成员变量,成员属性
 int m_r;//半径
   //称为:成员函数,成员方法
 double Perimeter(void);
 void setR(int r);

};

double Circle::Perimeter(void) {
 return 2 * PI * Circle::m_r;
}
void Circle::setR(int r) {
 Circle::m_r = r;
}
void test01() {
 Circle circle;
 /*circle.m_r=10;*/
 circle.setR(10);//可以设置一个函数去 *** 作类里的数据
 cout << circle.Perimeter() << endl;
}
内联函数
//宏函数的缺陷
//1.必须加括号保证运算符的完整性
#define MYADD(x,y)   ((x)+(y))  //打印结果600
//#define MYADD(x,y)   x+y   //打印结果410
void test02() {
 int a = 10;
 int b = 20;
 int ret = MYADD(a, b) * 20;
 cout << ret << endl;

}
//2. 即便加了括号,有些运算依然与预期运算结果不符
#define MYCOMPARE(a,b)  ((a)<(b)?(a):(b))
void test03() {
 int a = 10;
 int b = 20;
 int ret = MYCOMPARE(++a, b);//预期结果是11结果为12
 cout << ret << endl;

}
//预处理宏,内联函数,inline和普通函数性质一样,
//但会在适当的地方像预定义宏一样展开
//不会入栈出栈函数开销,以空间换时间
//注意事项:声明和定义必须都是用inline关键字,否则和普通函数一样
inline void func();
inline void func() {};
//类内部的内联函数,
//关于类内部的内联函数不是必须的,因为系统自动编译成内联函数
//有些好的编译器不用刻意加,会自动加,其他除类的也会加
C++内联函数的一些限制,否则编译器可能考虑不会将函数进行内联编译
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数进行取址操作

五、参数缺省函数重载
形参
//形参可以直接赋值,缺省都行
//int func(int a = 10, int b = 20);  声明和定义不能同时赋值
int func(int a = 10, int b = 20)
{
 return a + b;
}

void test01() {
 cout << func(30) << endl;
}
占位参数

//占位参数
//占位参数用时必须赋值(一般为了整齐先占个位置,后续再继续写,区分前置后置重载有用到)
//讲解
void func2(int a, int){

}
//用法1:占位不赋值
//这种占位参数,本参数往右后续不可以缺省
void  func3(int a, int, int c,int d) {

}
//用法2:占位赋值
//缺省但只可以用作最后一位形参占位,可以缺省该位
void  func4(int a, int = 1) {

}
void test02() {
 func2(10, 1);
 func3(10, 1, 20, 30);
 func4(10);
}
函数重载
//函数重载
//注意:1.函数重载不能出现两个完全一样的函数
//      2.指的是同一作用域下,同名的函数
//      3.参数类型不同,或顺序不同
//      4.无法重载返回值类型不同的函数
//可以根据参数自动选择函数


//4.无法重载返回值类型不同的函数
/*
int func() {
 cout << "func()调用" << endl;
 return 0;
}
*/
void func() {
 cout << "func()调用" << endl;
}

void func(int a) {
 cout << "func(int a)调用" << endl;
}

void func(double a,int b) {
 cout << "func(double a,int b)调用" << endl;
}

void func(int a,double b) {
 cout << "func(int a,double b)调用" << endl;
}

void test03() {
 func();
 func(1);
 func(2.0, 1);
 func(1,2.0);

}
函数重载其他两个二义性问题
//函数重载中 引用两个版本\
注意: my_func(int a)和 my_func(int& a)会出现二义性
/*
void my_func(int a){
 cout << "my_func(int a)调用" << endl;
}
*/
void my_func(int& a){
 cout << "my_func(int& a)调用" << endl;
}

void my_func(const int& a) {
 cout << "my_func(const int& a)调用" << endl;
}

//另一种二义性,给函数参数初始值
void my_func01(int a) {
 cout << "my_func(int a)调用" << endl;
}

void my_func01(int a ,int b = 10) {
 cout << "my_func(int a ,int b = 10)调用" << endl;
}
void test04() {
 int a = 10;
 /*const int b = 10;*/
 my_func(a);
 /*my_func(b);*/
 //or  const 分配空间 temp再赋值
 my_func(10);

 //另一种二义性
 my_func01(10, 20);//这种没问题
 //my_func01(10);  //函数缺省带来的二义性问题
}


六、C++兼容C语言调用
一种方式,只调用其中某个函数
//extern "C" void show();

void test01() {
 show();//在C++中有函数重载会修饰函数名,但是show
        //是C语言文件,因此链接失败
}
第二种 _ _ c plus plus 
在C的.h文件中
#ifdef __cplusplus
extern "C" {
#endif
#include 

void show();

#ifdef __cplusplus
}
#endif
类的封装

C++封装理念:
1.将属性(变量)和行为(函数)作为一个整体,来表现生活中的事务
2.将属性和行为加以权限控制

struct 和 class区别
class 默认权限为私有
struct 默认权限为公共(也可以用public:等权限 *** 作域符)

 *** 作权限:
public:      //公共
private:     //私有 友元可以访问
protected:   //保护 儿子可以访问父亲的权限

七、构造和析构
构造函数:初始化
析构函数:清理,恢复
编译器如果不用,自己配置个空的。
该两个函数编译器自动调用
class Person {
public://构造和析构必须声明在全局作用
 //构造函数
 //没有返回值  不用写void
 //函数名与类名相同
 // 可以有参数,可以发生重载,注意:重载后没写默认构造函数直接定义对象会报错
 //构造函数由编译器自动调用,且自动调用一次,无需手动调用
 Person()
 {
  cout << "Person的构造函数使用\n" << endl;
 }

 //析构函数
 //没有返回值 不用写void
 // 函数名与类名相同 函数名前加~
 // 不可以有参数,不可以发生重载
 // 析构函数也是由编译器自动调用,无需手动调用
 ~Person()
 {
  cout << "~Person的构造函数使用\n" << endl;
 }
};

void test01() {
 Person p;
}
构造函数的分类及调用
注意:析构函数的生命周期Person的作用域,也就是Person被释放后调用
//构造函数的分类及调用
//按照参数分类:无参构造函数(默认构造函数)和有参构造
//按照类型分类:普通构造函数    拷贝构造函数
class Person
{
public:
 int m_age;

 Person() {
  cout << "Person的构造默认函数使用\n" << endl;
 }
 Person(int age) {
  m_age = age;
  cout << "Person的构造有参函数使用\n" << endl;
 }


 //拷贝构造函数
 Person(const Person &p)
 {
  m_age = p.m_age;
 }
};

void test01() {
 Person p(18);
 Person p2(p);
 cout << "P2年龄:" << p2.m_age << endl;
}
注意:1.释放先释放p4,也就是先调用P4的析构函数,栈的后进先出原则
          2.Person(10);//单独拿出来 匿名对象   特点:当前行执行完后 立即释放
          3.不要用括号法 调用无参构造函数 Person P3(); 编译器认为是函数声明
          4.不要用拷贝构造函数 初始化 匿名对象
//构造函数的分类及调用
//按照参数分类:无参构造函数(默认构造函数)和有参构造
//按照类型分类:普通构造函数    拷贝构造函数(const Person & p)//防止被修改
class Person
{
public:
 int m_age;

 Person() {
  cout << "Person的构造默认函数使用\n" << endl;
 }
 Person(int age) {
  m_age = age;
  cout << "Person的构造有参函数使用\n" << endl;
 }


 //拷贝构造函数
 Person(const Person &p)
 {
  m_age = p.m_age;
  cout << "Person的拷贝构造有参函数使用\n" << endl;
 }

 //析构函数
 ~Person()
 {
  cout << "Person的析构函数使用\n" << endl;
 }
};


//构造函数的调用
void test01() {
 //1.括号法
 //Person p;
 //Person p1(10);
 //Person p2(p);

 //注意事项一:不要用括号法 调用无参构造函数 Person P3(); 编译器认为是函数声明
 //函数体内可以声明函数
 //void test02();
 //被编译器当成函数的声明
 /*Person p3();*/


 //2.显示法  
 Person p3 = Person(10);//有参构造(Person(10)作为右值不会被调用)
 Person p4 = Person(p3);//拷贝构造(Person(p3)作为右值不会被调用)
 /*注意释放先释放p4,也就是先调用P4的析构函数,栈的后进先出原则*/

 Person(10);//单独拿出来 匿名对象   特点:当前行执行完后 立即释放

 cout << "test01\n" << endl;

 //注意事项二:不要用拷贝构造函数 初始化 匿名对象
 /*Person(p3);*///p3重定义,编译器认为 Person p3对象实例化,如果已经有p3,p3就重定义



 //3.隐式法   一般不建议使用,可读性较低
 Person p5 = 10;   //相当于Person p5 = Person(10);
 Person p6 = p5;   //相当于Person p6 = Person(p5);
}

八、构造函数的调用
拷贝构造函数调用
class Person
{
public:
 int m_age;

 Person() {
  cout << "Person的构造默认函数使用\n" << endl;
 }
 Person(int age) {
  m_age = age;
  cout << "Person的构造有参函数使用\n" << endl;
 }


 //拷贝构造函数
 Person(const Person& p)
 {
  m_age = p.m_age;
  cout << "Person的拷贝构造有参函数使用\n" << endl;
 }

 //析构函数
 ~Person()
 {
  cout << "Person的析构函数使用\n" << endl;
 }
};
//1.用已经创建好的对象初始化新的对象
void test01() {
 Person p1(18);
 Person p2= Person(p1);
}
//2.用值传递的方式,给函数参数传值
void dowork(Person p) {

}
void test02() {
 Person p1(100);
 dowork(p1);//也会调用拷贝构造函数
}
//3.以值的方式返回局部对象
//debug版本可以,Release不可以,会被优化
Person dowork2() {
 Person p;
 return p;
}

//构造函数调用规则
//编译器会给一个类,至少添加3个函数 
//默认构造(空实现)    析构函数(空实现)   拷贝构造(所有代码值拷贝)
//如果我们自己提供了 有参数构造函数,编译器就不会提供默认构造函数,但是依然会提供拷贝构造函数
class Person {
public:
 int m_age;
 Person()
 {
  cout << "默认构造函数调用" << endl;
 }

 Person(int age)
 {
  m_age = age;
  cout << "有参构造函数调用" << endl;
 }

 Person(const Person &p)
 {
  m_age = p.m_age;
  cout << "拷贝构造函数调用" << endl;
 }
 ~Person()
 {
  cout << "析构函数调用" << endl;
 }
};

void test01() {
 Person p1;
 p1.m_age = 20;

 Person p2(p1);

 cout << "P2的年龄为:" << p2.m_age << endl;
}

九、深拷贝和浅拷贝的问题
浅拷贝值传递,析构函数重复释放malloc
问题:
有属性存放在堆区,利用编译器提供的拷贝构造函数会调用得到的钱拷贝构造重复释放堆区内存问题
解决:
用深拷贝方式代替浅拷贝,自己定义一个拷贝构造函数
class Person {
public:
 int m_age;
 char* m_name;
 Person(const char *name,int age)
 {
  m_name = (char*)malloc(strlen(name) + 1);
  strcpy(m_name, name);
  m_age = age;
 }
 
 //浅度拷贝构造函数
 //Person(const Person& p)
 //{
 // m_age = p.m_age;
 // cout << "拷贝构造函数调用" << endl;
 //}
 //深度拷贝构造函数
 Person(const Person &p)
 {
  m_name = (char*)malloc(strlen(p.m_name) + 1);
  strcpy(m_name, p.m_name);
  m_age = p.m_age;
 }

 ~Person()
 {
  if (m_name != NULL)
  {
   cout << "析构函数调用" << endl;
   free(m_name);
   m_name = NULL;
  }
  
 }
};

void test01() {

 Person p1("阿巴", 20);
 cout << p1.m_name<<"的年龄为:" << p1.m_age << endl;
 Person p2(p1);
 cout << p2.m_name << "的年龄为:" << p2.m_age << endl;
}
//初始化列表方法
class Person {
public:
 //初始化列表方法
 // 1.
 // Person(int a ,int b ,int c )
 // {
 //  m_a = a;
 //  m_b = b;
 //      m_c = c;
 // }
 // 2.
 //Person():m_a(10),m_b(20),m_c(30)
 //{
 //}
 // 3.
 Person(int a, int b, int c) :m_a(a), m_b(b), m_c(c)
 {
 }
 
 
 ~Person()
 {

 }
 int m_a;
 int m_b;
 int m_c;
};

void test01() {
 Person p(10,20,30);
 cout << p.m_a << endl;
 cout << p.m_b << endl;
 cout << p.m_c << endl;
}

十、类对象作为类成员
//当其他类作为类成员,作为本类成员,先构造其他对象,再构造自身
//C++先构造零件即包含对象
class Phone {
public:

 Phone(string pName)
 {
  cout << "Phone有参函数构造调用" << endl;
  m_phone_name = pName;
 }

 ~Phone()
 {
  cout << "Phone析构函数构造调用" << endl;
 }

 string m_phone_name;
};

class Game {
public:

 Game(string gName)
 {
  cout << "Game有参函数构造调用" << endl;
  m_game_name = gName;
 }

 ~Game()
 {
  cout << "Game析构函数构造调用" << endl;
 }

 string m_game_name;
};

class Person {
public:

 Person(string name, string pName, string gName):m_name(name), m_phone(pName), m_game(gName)
 {
  cout << "Person有参函数构造调用" << endl;
 } 
 
 void Play_Game()
 {
  cout << "姓名:" << m_name << "  手机:" << m_phone.m_phone_name << "  游戏:" << m_game.m_game_name << endl;
 }

 ~Person()
 {
  cout << "Person析构函数构造调用" << endl;
 }
 string m_name;
 Phone m_phone;
 Game m_game;
};

void test01() {
 Person p("TOM", "iPhone", "王者");
 cout << "姓名:" << p.m_name << "  手机:"<< p.m_phone.m_phone_name << "  游戏:" << p.m_game.m_game_name << endl;
 p.Play_Game();
}
explicit
//explicit 用途,防止利用隐式方法构造对象
class MyString {
public:
 MyString(char* str)
 {

 }

 //
 explicit MyString(int len)
 {

 }
};

void test01() {
 MyString(10);
 //MyString = 10;用explicit不可以
}

视频链接https://www.bilibili.com/video/BV1kV411U7Ub?spm_id_from=333.999.0.0 建议用python或者浏览器视频插件把视频先下下来,万一没了呢!!!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/741854.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存