c++基础-待更新

C++基础-00

00-GCC与G++编译

1.编译步骤

该编译器编译程序的四个步骤:预处理-> 编译->汇编->链接(test.c test.h -> test.i -> test.s->test.o->test)

  • 预处理 gcc -E test.c -o test.i
  • 编译 gcc -S test.i -o test.s
  • 汇编 gcc -c test.s -o test.o
  • 链接 gcc test.o -o test
  • 最后生成可执行文件test(linux运行以下命令 ./test)

2.GCC与G++的区别

首先这两个都是编译器,只不过一个主要用来编译c,另一个主要用来编译c++。

  • 对于 .c和.cpp文件,gcc分别当做c和cpp文件编译
  • 对于 .c和.cpp文件,g++则统一当做cpp文件编译
  • 使用g++编译文件时,g++会自动链接标准库STL,而gcc不会自动链接STL
  • gcc在编译C文件时,可使用的预定义宏是比较少的

3.实例

01 -基础部分

1.1 开始第一个程序:hello world !

#include <iostream>
using namespace std;
int main()
{
    // << 为左移运算符
    cout << "Hello World!" << endl;
    //system("pause") 暂停跳出来的黑窗口
    reutrn 0;
}

1.2 ::双冒号作用域运算符

#include<iostream>
using namespace std;

int atk = 200;
void test01()
{
	int atk = 100;
	cout << "攻击力为 : " << atk << endl;//100
	//双冒号 作用域运算符  ::全局作用域
	cout << "全局攻击力为 : " << ::atk << endl;//200
}

int main(){
	test01();
	//system("pause");
	return 0;
}

1.3 namespace 命名空间

  • 用来解决冲突(这里的冲突指的是命名冲突)问题,不同的作用域中的变量或者函数等等名字可能是相同的,但是实际的功能是不同的。(后面几个为.h文件与侯捷老师提倡的写法不太一样)

    #include <iostream>
    #include <game1.h>
    #include <game2.h>
    void test01() 
    {
        LOL::goAtk();
        KingGlory::goAtk();
    }
    
    int main()
    {
        test01();
        return 0;
    }
    

    game1.h

    #include <iostream>
    using namespace std;
    
    namespace LOL 
    {
        void goAtk();
    }
    

    game1.cpp

    #include <game1.h>
    void goAtk()
    {
        cout << "LOL攻击实现" << endl;
    }
    

    game2.h

    #include <iostream>
    using namespace std;
    
    namespace KingGlory
    {
        void goAtk();
    }
    

    game2.pp

    #include <game1.h>
    void goAtk()
    {
        cout << "王者农药攻击实现" << endl;
    }
    
  • 必须在全局作用域下声明,定义在任何函数体内都是错误的。

  • 命名空间下可以放入函数、变量、结构体、类...

    namespace A
    {
        void func();
        int m_A = 20;
        struct Person
        {
    	};
        class Animal{};
    }
    
  • 命名空间可以嵌套命名空间

    namespace A
    {
        int m_A = 20;
        namespace B
        {
            int m_A = 10;
    	}
    }
    
    void test02()
    {
        cout << "作用域A下的m_A:" << A::m_A << endl;
        cout << "作用域B下的m_A:" << A::B::m_A << endl;
    }
    
  • 命名空间是开放的,可以随时加入新的成员

    namespace A  
    {
    	int m_A = 100;
    }
    
    namespace A  //此A命名空间会和上面的命名空间A进行合并
    {
    	int m_B = 1000;
    }
    
    void test03()
    {
        cout << "作用域A下的m_A:" << A::m_A << endl;
        cout << "作用域B下的m_B:" << A::m_B << endl;
    }
    
  • 匿名空间的效果等于用static来修饰

    namespace
    {
        //当写了无名命名空间,相当于写了 static int m_C ; 		//static int m_D;
    	//只能在当前文件内使用
    	int m_C = 0;
    	int m_D = 0;
    }
    
  • 命名空间可以用别名来替代

    namespace veryLongName
    {
    	int m_A = 0;
    }
    void test04()
    {
    	//换成另外一个名字,结果是一样的
    	namespace veryShortName = veryLongName;
    	cout << veryLongName::m_A << endl;
    	cout << veryShortName::m_A << endl;
    }
    

using 声明和using编译命令

  • 在声明和使用的过程中需要注意避免二义性如:

    #include<iostream>
    using namespace std;
    namespace KingGlory
    {
    	int sunwukongId = 10;
    }
    void test01()
    {
    	int sunwukongId = 20;
    	//using 声明  注意避免二义性问题
    	//写了using声明后  下面这行代码说明以后看到的		sunwukongId 是用KingGlory下的
    	//但是  编译器有就近原则
    	//二义性
    	using KingGlory::sunwukongId;
    	cout << sunwukongId << endl;
    }
    
    //using编译指令
    namespace LOL
    {
    	int sunwukongId = 30;
    }
    void test02()
    {
    	//int sunwukongId = 20;
    	//using编译指令
    	using namespace KingGlory; //打开王者荣耀房间
    	using namespace LOL;//打开LOL房间
    	//如果打开多个房间,也要避免二义性问题
    	cout << LOL::sunwukongId << endl;
    }
    

1.4 c++对c的扩展(区别),不包含新标准

  • 全局变量增强,c++不允许重复声明

    int a; int a = 20; //错误
    
  • 函数检测增强,c++要求指定形参的类型和个数,且必须要有返回参数(一般情况下不会乱写)

  • 类型转换检测增强

    char *p =  (char*) malloc(sizeof(64))
    
  • struct 增强,C语言struct不允许加函数

  • bool类型增强, C语言中没有bool类型

  • 三目运算符增强

  • const增强

    • C++中的const默认内部链接 ,extern提高作用域

const内存分布情况

  • const分配内存 取地址会分配临时内存
  • extern 编译器也会给const变量分配内存
  • 用普通变量初始化 const 的变量会分配内存
  • 自定义数据类型 加const也会分配内存

1.5引用的基本用法

  • 引用基本语法 Type &别名 = 原名(注意这里的&不是取地址)

    int a = 10;
    int &b = a;
    b = 20;
    cout << a << endl;
    cout << b << endl;
    int c = 30;
    b = c;
    cout << b << endl;
    
  • 引用必须初始化,引用初始化后不可以修改了

  • 对数组建立引用

    int arr[10];
    for (int i = 0; i < 10;i++)
    {
    	arr[i] = i;
    }
    
    int(&pArr)[10] = arr;
    for (int i = 0; i < 10;i++)
    {
    	cout << pArr[i] << " ";
    }
    cout << endl;
    

1.6 函数参数的传递方式:值传递、地址传递、引用传递

void mySwap(int a, int b)
{
	int tmp = a;
	a = b;
	b = tmp;

	cout << "mySwap::a = " << a << endl;
	cout << "mySwap::b = " << b << endl;
}

void test01()
{
	int a = 10;
	int b = 20;
	mySwap(a, b); //值传递

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}
void mySwap2(int * a, int * b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void test02()
{
	int a = 10;
	int b = 20;
	mySwap2(&a, &b); 

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}
void mySwap3(int &a, int &b)  //&a = a &b = b
{
	int tmp = a;
	a = b;
	b = tmp;
}

void test03()
{
	int a = 10;
	int b = 20;
	mySwap3(a, b); 

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}

指针的引用,这部分比较懵逼


常量引用,简单地介绍了const的几个应用场景

void test01()
{
	//int &ref = 10;//引用了不合法的内存,不可以
	const int &ref = 10; //加入const后 ,编译器处理方式为: int tmp = 10; const int &ref = tmp;
	//ref = 10;
    /* 下面最好别这么干,因为const表明的意思是不希望修改这个值*/
	int * p = (int*)&ref;
	*p = 1000;

	cout << "ref = " << ref << endl;

}

//常量引用使用场景 用来修饰形参
void showValue(const int &val)
{
	//val += 1000; //如果只是想显示内容,而不修改内容,那么就用const修饰这个形参
	cout << "val = " << val << endl;
}

void test02()
{
	int a = 10;
	showValue(a);
}

02-面向对象特性

2.1 面向对象的三大特征:封装、多态、继承

C++中的封装 严格类型转换检测, 让属性和行为 绑定到一起
1 .属性和行为作为一个整体来表示生活中的事物
2 .控制权限 public 公有权限 protected 保护权限 private 私有权限

struct 和class是一个意思,唯一的不同 默认权限 ,struct是public,但是class默认权限是private,为了方便将最好将成员变量设置为private

1.public 类内 类外 都可以访问
2.protected 类内可以访问 类外 不可以访问 (子类可以方法)
3.private 类内可以访问 类外 不可以访问 (子类不可以方法)

2.2 构造函数相关

  • 构造函数与析构函数

    class Person
    {
    public:
    	//构造函数写法
    	//与类名相同,没有返回值 ,不写void,可以发生重载 (可以有参数)
    	//构造函数由编译器自动调用,而不是手动,而且只会调用一次
        //如果没有自定义的构造函数,编译器会自动生成一个构造函数并调用
    	Person()
    	{
    		cout << "构造函数调用" << endl;
    	}
    
    	Person(int a)
    	{
    		cout << "构造函数调用(int a)" << endl;
    	}
    
    	//析构函数写法
    	// 与类名相同 类名前面加一个符号 “~” ,也没有返回值 ,不写void, 	不可以有参数(不能发生重载)
    	//自动调用,只会调用一次
    	~Person()
    	{
    		cout << "析构函数调用" << endl;
    	}
    };
    
  • 构造函数分类,按参数可分为无参构造函数(默认的)和有参构造函数

    按类型可分为普通构造函数和拷贝构造函数。

    #include<iostream>
    using namespace std;
    
    class Person
    {
    public: 
    	Person() //默认、无参构造函数
    	{
    		cout << "默认构造函数调用" << endl;
    	}
    
    	Person(int a)
    	{
    		cout << "有参构造函数调用" << endl;
    	}
    
    	//拷贝构造函数
    	Person(const Person& p)
    	{
    		m_Age = p.m_Age;
    		cout << "拷贝构造函数调用" << endl;
    	}
    
    	~Person()
    	{
    		cout << "析构函数调用" << endl;
    	}
    
    	int m_Age;
    };
    
    void test01()
    {
    	//构造函数调用方式
        //括号法调用
    	Person p1(1); //有参
    	Person p2(p1); //拷贝
    
    	Person p3; 
        //默认构造函数不要加() Person p3(); 
        //编译器认为这行是函数的声明
    
    	//显示法调用
    	Person p4 = Person(100);
    	Person p5 = Person(p4);
    
    	Person(100); //叫匿名对象 ,匿名对象特点,如果编译器发现了对象是匿名的,那么在这行代码结束后就释放这个对象
    
    	//不能用拷贝构造函数 初始化匿名对象
    	Person p6 = Person(p5); 
        //如果写成左值,编译器认为你写成Person p5; 
        //对象的声明,如果写成右值,那么可以
    
    	Person p7 = 100; 
        //相当于调用了Person p7 = Person(100) 
        //隐式类型转换
    	Person p8 = p7; //相当于Person p8 = Person(p7);
    }
    
    int main()
    {
    	test01();
    	return 0;
    }
    
  • 系统默认给一个类提供 3个函数 默认构造 、 拷贝构造 、 析构函数

    1. 当我们提供了有参构造函数,那么系统就不会在给我们提供默认构造函数了,但是 系统还会提供默认拷贝构造函数 , 进行简单的值拷贝
    2. 当我们提供了 拷贝构造,那么系统就不会提供其他构造了
  • 列表的初始化,我们可以直接在构造函数的后面初始化数据,建议使用这种方法来初始化数据。

    class Person
    {
    public:
        Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c) {}
    private:
    	int m_A;
    	int m_B;
    	int m_C;
    };
    
  • 系统会提供默认拷贝构造,而且是简单的值拷贝(浅拷贝,两者所指向的内存是一样的),如果定义的类中需要访问其他资源,比如动态分配内存、指向其他数据的指针等,我们必须定义拷贝构造函数,以完整地拷贝对象的所有数据。(这样我们修改其中一个对象的一些数据的时候,不会对另一个产生影响)

    class Base
    {
    public:
        Base(): m_a(0), m_b(0){}
        Base()(int a, int b) : m_a(a), m_b(b) {}
    private:
        int m_a;
        int m_b;    
    };
    int main()
    {
        int a = 10;
        int b = a; //浅拷贝
        Base obj1(10, 20);
        Base obj2 = obj1; //浅拷贝
    }
    
    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    //变长数组
    class Array
    {
    public:
        Array(int len);
        Array(const Array &arr);
        ~Array();
        int operator[](int i) const {return m_p[i];}
        int &operator[](int i) {return m_p[i];}
        int length() const {return m_len;}
    private:
        int m_len;
        int *m_p;
        
    };
    Array::Array(int len): m_len(len)
    {
        m_p = (int*)calloc(len, sizeof(int));
    }
    Array::Array(const Array &arr)
    {  //拷贝构造函数
        this->m_len = arr.m_len;
        this->m_p = (int*)calloc(this->m_len, sizeof(int) );
        memcpy(this->m_p, arr.m_p, m_len * sizeof(int) );
    }
    //析构函数,释放之前开辟的内存,避免内存泄漏
    Array::~Array(){ free(m_p); }
    //打印数组元素
    void printArray(const Array &arr)
    {
        int len = arr.length();
        for(int i = 0; i < len; i++){
            if(i == len - 1){
                cout<< arr[i] << endl;
            }else{
                cout << arr[i] << ", ";
            }
        }
    }
    
    void test01()
    {
    	Array arr1(10);
        for(int i=0; i<10; i++)
        {
            arr1[i] = i;
        }
       
        Array arr2 = arr1;
        arr2[5] = 100;
        arr2[3] = 29;
        
        printArray(arr1);
        printArray(arr2);
    
    }
    int main()
    {
        //这里的输出结果是不一样的,因为分配到的内存地址不一样
        //如果是浅拷贝的话,值是一样的,同时析构函数会发生冲突
        test01();
        
        return 0;
    }
    
    

2.4 类对象作为成员

  • 类对象作为类成员时候,构造顺序先将类对象一一构造,然后构造自己, 析构的顺序是相反的

    class Phone
    {
    public:
    	Phone()
    	{
    		cout << "手机的默认构造函数调用" << endl;
    	}
    	Phone(string name)
    	{
    		cout << "手机的有参构造调用" << endl;
    		m_PhoneName = name;
    	}
    	
    	~Phone()
    	{
    		cout << "手机的析构函数调用" << endl;
    	}
    
    	string m_PhoneName;
    
    };
    
    class Game
    {
    public:
    	Game()
    	{
    		cout << "Game的默认构造函数调用" << endl;
    	}
    	Game(string name)
    	{
    		cout << "Game的有参构造调用" << endl;
    		m_GameName = name;
    	}
    	~Game()
    	{
    		cout << "Game的析构函数调用" << endl;
    	}
    
    	string m_GameName;
    
    };
    
    class Person
    {
    public:
    	Person()
    	{
    		cout << "Person的默认构造函数调用" << endl;
    	}
    
    	Person(string name, string phoneName, string gameName) : m_Name(name), m_Phone(phoneName), m_Game(gameName)
    	{
    		cout << "Person的有参构造调用" << endl;
    		//m_Name = name;
    	}
    
    	void playGame()
    	{
    		cout << m_Name << " 拿着《" << m_Phone.m_PhoneName << "》牌手机 ,玩着《" << m_Game.m_GameName << "》游戏" << endl;
    	}
    
    	~Person()
    	{
    		cout << "Person的析构函数调用" << endl;
    	}
    
    	string m_Name; //姓名
    	Phone m_Phone; //手机
    	Game m_Game; //游戏
    };
    
    

2.4 explicit关键字 ,防止隐式类型转换

2.5 new和delete运算符的使用

posted @ 2021-05-26 20:43  mujiaa  阅读(78)  评论(0)    收藏  举报