C++

来源于慕课网教程
https://www.imooc.com/u/1349694/courses?sort=publish

一.

  1. 随用随定义变量
int v1 = 1;
v1 = v1 + 1;
int v2 = 2;  // v1 v2在用到的时候才定义
v2 = v2 + v1;

  1. cin >> x >> y; 不能cin >> x , y;

cout << oct << x ; 以八进制输出x
cout << dec << x ;十进制
cout << hex << x ;十六进制

  1. namesapce用于创建命名空间。using namespace xxx用于使用某个命名空间,可以直接用里面的变量,代码,而不需要再加xxx::。如下面的fun2的使用
#include <iostream>
#include <stdlib.h>
using namespace std;

namespace A {
	int x = 1;
	void fun() {
		cout << "A" << endl;
	}
}

namespace B {
	int x = 2;
	void fun() {
		cout << "B" << endl;
	}
	void fun2() {
		cout << "this is fun2" << endl;
	}
}

using namespace B;
int main() {
	cout << "A的变量x=" << A::x << endl;
	B::fun();
	fun2();
}
  1. C++中数组作为参数传递
    数组传给函数的是数组名,实际上是数组的首地址。
int fun(int a[]) {
	return *(a + 1);  // 2  也可以a[1]
}

int fun2(int *p) {
	return p[1];  // 2  也可以p[1]
}

int main() {
	int my_array[3] = { 1,2,3 };
	cout << fun2(my_array) << endl;
	return 0;
}
  1. 指针类型的引用
    类型 *& 别名 = 指针; (从右往左读,首先类型是引用,然后加上*类型修饰符表示是对指针的引用)
    int a = 1;
    int *p = &a;
    int *&name = p;
    
  2. const限定符
    除了书上的,讲一些特殊的情况。
    (1)
int x = 0;
const int *p = &x;
*p = 1; // 错误,p被限定为指向常量,在p指针眼里,x是常量,所以不能用指针改变值。

(2)在一个文件中定义一个常量,然后让其他文件通用。需要在定义声明的时候都加extern关键字
在file1.cpp文件中定义extern const int buffersize = 100;
在file2.cpp文件中声明extern const int buffersize;

  1. 函数默认值
    一般在声明的时候加上默认值,定义的时候不加。

    void fun(int i, int j = 5, int k =10);  // 声明
    .......
    void fun(int i, int j, int k){    // 定义
    ......
    .....
    }
    
  2. 函数重载
    同一命名空间下的同名函数。
    int getMax(int x, int y, int z){ // to do}
    double getMax(double x, double y){ // to do}
    在编译时会生成getMax_int_int_int和getMax_double_double这两个函数,根据传入的参数来调用这两个函数。

  3. 内联函数
    内联函数在定义时加inline关键字。内联函数通常就是将它在程序中的每个调用点上“内联地”展开。调用普通的函数相对比较慢,会保存寄存器之类,调用内联函数则相当于直接把函数的代码在调用处写出来。

inline max(int a, int b); //声明

int main(){
int x = 1, y = 2, m;
m = max(x, y);  //调用内联函数,与调用普通函数一样
}

/*
等价于
int main(){
int x = 1, y = 2, m;
int a, b;
a = x;
b = y;
m = a>b?a:b;
}
*/

//定义
inline max(int a, int b){
return a>b?a:b;
}
  1. 内存管理
    (1)申请/释放内存
   int *p = new int; // 申请一个字节内存, new int返回一个指针指向申请的首地址。
   //也可以int *p = new int[10]; 直接为该地址初始化值10。
  //申请成功了吗
   if (p == NULL) {
	//申请失败,异常处理
	}
   *p = 10; //赋值
   delete p; // 释放
   p = NULL; // 使指针为空,否则还会指向地址。

	// 申请块内存
   int *p = new int[max]; // 申请一块内存,十个字节。返回一个整形指针。
   //申请成功了吗
   if (p == NULL) {
	//申请失败,异常处理
	}
   p[0] = 1; p[1] = 2; 
   delete []p; // 释放一块内存
   p = NULL;
   
	
  1. 类和对象
    (1)创建类和实例
    访问限定符
    public
    private
    protected
    创建实例的两种方法:从栈创建,从堆(内存)创建
class TV{  //  类名后没有冒号:
public:
int number;
void fun(){pass}
};  // 分号不要忘了

//栈创建
TV tv;
tv.number = 0;
tv.fun();

//堆创建 就是申请内存
TV *p = new TV();
TV *q = new TV[20]; // 创建20个实例 
p -> number = 0;  // 调用成员的方式不同
p -> fun();
delete p;
p = NULL;

for(int i = 0; i < 20; i++){
	q[i] -> number = 0;
	q[i] -> fun();
}
delete []q;
q = NULL;

(2)数据封装
通过成员函数来实现功能,让外界尽量不能直接调用成员变量。
成员变量命名规范: m_strName m_iName m下划线类型名字(名字首字母大写)
成员函数中与成员变量对应的形参 :_name 下划线名字。

// 类的成员变量全是私有,外界不能直接访问。通过成员函数实现所有功能。
class student {
public:
	void setname(string _name) {
		m_strName = _name;
	}
	void getname(int _number) {
		m_iNumber = _number;
	}
private:
	int m_iNumber;
	string m_strSex;
	string m_strName;
};

(3)类外定义
成员函数在类外定义(实现)。但函数要现在类内声明
a. 同文件类外定义
成员函数与类在同一个文件中,成员函数在类外定义。
假设在test.cpp文件中

class student {
public:
	void setname(string _name);  //先声明
	string getname();
private:
	int m_iNumber;
	string m_strSex;
	string m_strName;
};

void student::setname(string _name) {   //类名加双冒号
	m_strName = _name;
}

string student::getname() {
	return m_strName;
}

b.分文件类外定义 (推荐)
把类的定义写在头文件中,类内只有成员函数的声明。文件名:类名.h
把成员函数的定义写在cpp文件中。文件名:类名.cpp

类的头文件
在这里插入图片描述
类的源文件
在这里插入图片描述
主函数文件
在这里插入图片描述
在主函数文件中只用包含类的头文件。不用包含类的源文件。

(3)对象结构
在这里插入图片描述
(4)构造函数
a. 构造函数在对象实例化时被自动调用;
b. 与类同名;
c. 没有返回值,所以连void都不用写;
d. 可以有多个重载形式;
e. 实例化对象时仅用到一个构造函数;
f. 当用户没有定义构造函数时,编译器会自动生成构造函数。
g.默认构造函数:函数没有参数或者每个参数都有默认值。

//  student.h文件

#include <string>
using namespace std;
class student {
private:
	int m_iAge;
	string m_strName;
public:
	student();
	student(int _age, string _name);
	int getage();
	string getname();
};


// student.cpp文件

#include "student.h"
student::student() {
	m_iAge = 20;
	m_strName = "chen";
}

student::student(int _age, string _name) {
	m_iAge = _age;
	m_strName = _name;
}

int student::getage() {
	return m_iAge;
}

string student::getname(){
	return m_strName;
}

// 在主函数中 
student s1;
student s2(120, "rui");  //  会分别调用不同的构造函数(构造函数重载)

(5)初始化列表下的构造函数(推荐)

a. 初始化列表先于构造函数执行
b. 初始化列表只能用于构造函数
c. 初始化列表可以同时初始化多个数据成员
d. 成员变量由const修饰时,要想修改成员变量的值,必须只能写在初始化列表中。

class TV{
private:
	int m_iAge;
	string m_strName;
	const int m_iNumber = 5; // m_iNumber只能通过写在初始化列表中改变值。
public:
	TV();
	TV(int _age, string _name, int _number);
};

TV::TV():m_iAge(120), m_strName("chen"), m_iNumber(30) {}  
// 在构造函数的括号和大括号之间加列表。
//把const常量的赋值写在构造函数内会失败。
// 成员的括号内是构造函数的形参,直接填字面量会直接给成员赋值。
TV::TV(int _age, string _name, int _number):m_iAge(_age), m_strName(_name), m_iNumber(_number) {}

(6)拷贝构造函数
是一种特殊的构造函数。如果没写,会自动生成。

类名(const 类名 & 变量名){ do something}
注意:拷贝构造函数第一个参数不一定要有const ,加const只是为了安全起见。
比如说 X::X( X &){}也是拷贝构造函数。
默认构造函数是把被拷贝对象的值一个一个复制给被初始化对象

// 默认拷贝构造函数
STUDENT::STUDENT(const STUDENT &C){
age = C.age;
name = C.name;
number = C.number;
}

拷贝构造函数的使用场景

class STUDENT{
private:
	int m_iAge;
public:
	STUDENT(int _age): m_iAge(_age) {}
	STUDENT(const STUDENT &name){   // 引用
		m_iAge = name.m_iAge; // 完成指定的复制操作
	}
	
};

void fun1(STUDENT C){
cout << "test" << endl;
}

STUDENT fun2(){
STUDENT test(120);
return test;
}

// 使用到构造函数的场景
int main(){

// 场景一 实例通过另一个实例初始化
STUDENT s1(120); // 调用构造函数 
STUDENT s2 = s1; // 调用s2的拷贝构造函数 实际上对s1产生了一次引用。
// 形参中传入的是s1对象。const STUDENT &name = s1  然后把s1的值复制给s2

// 场景二 实例(对象)本身直接传入函数形参
// fun1函数是全局函数
fun1(s1); // 此处是 STUDENT C = s1  所以也调用了拷贝构造函数

// 场景三 函数返回值是实例(对象)
fun2(); // 返回的是实例test; 实际上是创造了一个用test对象初始化的临时变量(此处调用拷贝构造函数)。
// 返回临时变量。

return 0;
}

浅拷贝与深拷贝
https://blog.youkuaiyun.com/lwbeyond/article/details/6202256
浅拷贝仅进行最简单的赋值,涉及到动态变量(比如分配一段内存,或者计数器)时,就不行了。
默认拷贝构造函数就是执行的浅拷贝,会把对象的值一个一个拷给另外一个对象。

class student{
public:
	student(){
		m_iAge = 120;
		m_iPtr = new int(100); // 为指针创建一个字节的堆空间,值是100
	}
	~student(){
		if(m_iPtr != NULL)
			delete m_iPtr;
	}
private:
	int m_iAge;
	int *m_iPtr;

};

int main(){
student s1;
student s2 = s1; // 此时会出错。因为类只有默认构造函数。所以把s1的指针完全拷贝给s2.
// 此时s2有一个指针,指向和s1完全一样。当析构函数销毁指针时,会对同一个堆地址delete两次,产生错误。
return 0;
}

深拷贝,对于动态成员会重新分配空间。下面通过自定义拷贝构造函数实现深拷贝

class student{
public:
	student(){
		m_iAge = 120;
		m_iPtr = new int(100); // 为指针创建一个字节的堆空间,值是100
	}
	student(const student &C){
		m_iAge = C.m_iAge;
		m_iPtr = new int;  // 重新分配一块空间
		*m_iPtr = *(C.m_iPtr); // 存的值要一样
	}
	~student(){
		if(m_iPtr != NULL)
			delete m_iPtr;
	}
private:
	int m_iAge;
	int *m_iPtr;

};

int main(){
student s1;
student s2 = s1;  // 此时就不会出错了。s2和s1的指针值不同,但存的值相同。
return 0;
}

(7)析构函数
在对象销毁时会自动调用。比如用构造函数给指针分配了一段堆空间,就应该用析构函数销毁这段空间,析构函数确保了这段空间只有在对象没有利用价值时才被销毁。
a.没有返回值
b.不能重载
c.没有参数
格式:~类名()

class student{
private:
	int age;
	int *p;
public:
	student(): age(120) { 
		p = new int[20];
	}
	~student(){  // 析构函数 释放内存
		delete p;
		p = NuLL;
	}

};

int main(){
student *p = new student[10]; // 为指针分配十个堆空间 存放student类的实例
delete p;  // 释放内存 此时会调用析构函数
p = NULL;
return 0;
}

(8)对象数组

// 从栈中实例化对象数组
// 栈中实例化的对象系统会自动回收内存
Student stu[3]; // 此时三个对象都会调用构造函数初始化
stu[1].name = "chen";

// 从堆中实例化对象数组
Student *p = new Student[3];
(p+1) -> name ="chen"; // 两种访问方式一样
p[1].name = "chen";
delete []p; // delete []p会销毁整个对象数组  delete p只会销毁数组中第一个对象
p = NULL;

(9)对象成员
对象成员是指类中的某个成员是一另一个类的对象。比如直角坐标系中线段和点的关系。点是一个类,数据成员是x,y坐标。线段是一个类,由于线段由两个点确定,所以数据成员是两个点的实例。
实例化含对象成员的类时,会先实例化对象成员,再实例化本类的对象,销毁时顺序相反,就和造车要先造轮子再造车壳,拆车要先拆车壳,再拆轮子一样。

class nod {
private:
	int m_iX;
	int m_iY;
public:
	nod(int x, int y);
	void setX(int x);
	void setY(int y);
	int getX();
	int getY();
};
nod::nod(int x, int y): m_iX(x), m_iY(y){}
void nod::setX(int x) { m_iX = x; }
void nod::setY(int y) { m_iY = y; }
int nod::getX() { return m_iX; }
int nod::getY() { return m_iY; }


class line {
public:
	line(int x1, int y1, int x2, int y2);
	void setx1(int x, int y);
	void setx2(int x, int y);
private:
	nod m_nodX1;  // 对象成员,是nod类的实例
	nod m_nodX2;
};
// 构造函数中直接给对象成员初始化,会调用对象成员的构造函数
line::line(int x1, int y1, int x2, int y2):m_nodX1(x1, y1), m_nodX2(x2, y2){} 
void line::setx1(int x, int y) {
	m_nodX1.setX(x);  // 调用对象成员的成员函数
	m_nodX1.setY(y);
}
void line::setx2(int x, int y) {
	m_nodX2.setX(x);
	m_nodX2.setY(y);
}

(10)对象指针

// 从堆实例化指针
Student *stu1 = NULL;
stu1 = new Student(12,"chen");
// 或者
Student *stu1 = new Student(12, "chen");  //此时就可以初始化了

// 也可以用对象指针指向从栈实例化的对象
Student stu1(12, "chen");
Student *stu2 = &stu1;
stu2 -> m_iAge = 10;  // 指向对象的指针可以直接操作对象的成员
stu2 -> name = "chen";

(11)对象成员指针
对象成员指针是指类的某个对象成员是另外一个类的对象指针。与对象成员有点像,只不过此时的对象变成了指针。它更加节约空间,一个Coordinate对象有两个整型成员变量,所以一个该类对象占8字节,若果做为Line的对象成员,假设Line只有两个该对象成员,则一个Line类的实例占16字节。
使用对象成员指针,由于一个指针只占4字节,所以两个对象成员指针占8字节,所以一个Line类的实例占8字节。

class Coordinate{
private:
	int m_iX;
	int m_iY;
public:
	Coordinate(int x, int y){
		m_iX = x;
		m_iY = y;
	}
};

class Line{
private:
	Coordinate *m_coA;  // 这就是对象成员指针,一个指针占4字节
	Coordinate *m_coB;
public:
	Line(int x1, int y1, int x2, int y2){
		m_coA = new Coordinate(x1, y1);  
		m_coB = new Coordinate(x2, y2);
	}
	~Line(){
		delete m_coA;
		delete m_coB;
		m_coA = NULL;
		m_coB = NULL;
	}
};


(12)this指针
this指针指向对象自身,在一个类中this指针表示类的实例化对象。
比如函数参数和成员同名时,可以用this指针区分。

函数返回引用
https://www.jianshu.com/p/4f0a892c2f89

class Student{
private:
	int age;
public:
	Student(int age){
		this -> age = age; // this -> age是成员变量
	}
	void setage(int age){
		this -> age = age;
	}
	Student& getinfo(){  // 返回的是引用,而且是对当前对象的引用
		return *this;  // *this就是当前对象 因为this是指向当前对象的指针
	}
	
};

int main(){
Student stu(10);
stu.getinfo().setage(120);  // stu.getinfo()得到的是一个对stu对象的引用
						   // 通过该引用调用setage()与stu调用setage()效果一样。
						   // 如果getinfo()返回的不是对象的引用,就没这个效果

return 0;
}

将getinfo函数该成返回指针一样的作用
Student* getinfo(){
	return this;
}
stu.getinfo()->setage(120);

  1. c++中的输入
    (1)cin 最原始。使用cin时的>>符号会过滤掉看不见的字符,比如空格,回车,TAB。
char a[20];
cin >> a;
输入 abcdr aeqe
输出 abcdr
当有多个变量时,会将空白符后的输入给第二个变量。

(2)getline()与cin.getline()
前者是全局函数,在<string>中。接受一串字符,包括空格等。

string str;
getline(cin, str);
输入abcdef ghi
输出abcdef ghi

后者是<istream>中的函数。第一个参数是数组名,第二个是接受字符个数。可以接受空白符。第三个参数是结束符,默认以’\0’结束。

char a[20];
cin.getline(a, 5);
输入 abcdefg
输出abcd(\0算一个字符,输出的时候实际上看不到)
  1. string类型
    (1)初始化
#include <string>
using namespace std;

string s1; // 空字符串
string s2("abc"); string s2 = "abc";  // 字符串字面量
string s3(s2); string s3 = s2;  // s3是s2的副本
string s4(n, 'c');  // s4 = n个c

(2)常用操作
在这里插入图片描述
注意:单纯的字符串不能用+连接。只有表达式含有string变量时才可以。
"hello" + "world";是错误的用法。但 s1 + "world";是正确的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值