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个函数 默认构造 、 拷贝构造 、 析构函数
- 当我们提供了有参构造函数,那么系统就不会在给我们提供默认构造函数了,但是 系统还会提供默认拷贝构造函数 , 进行简单的值拷贝
- 当我们提供了 拷贝构造,那么系统就不会提供其他构造了
-
列表的初始化,我们可以直接在构造函数的后面初始化数据,建议使用这种方法来初始化数据。
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; //游戏 };

浙公网安备 33010602011771号