类和对象初步
目录
补充知识
引用&
基本定义
类型名 & 引用名=变量名,引用名相当于变量名的一个别名
定义时必须初始化成某个引用变量
只能引用变量,不能引用常量、const变量和表达式和⚠️迭代器(右值不允许绑定)
特殊的,对于const 引用(
const 类型名 &)- 定义时仍然必须初始化
- 但是可以绑定到变量、常量、const变量或表达式(此时将延长表达式值的周期)也可以引用迭代器
初始化后便一直引用该变量,不会引用其他变量
int a = 10; int b = 20; int &ref = a; // ref is now an alias for a ref = b; // This does NOT make ref refer to b! 仅仅只是将b的值赋给了a引用某个引用的引用也成为该原变量的别名
int* & ref=p;合法
引用作为函数返回值
!!!注意⚠️:此时可以对返回值赋值
#include<iostream>
using namespace std;
int n=4;
int & setvalue(){ return n;}
int main(){
setvalue()=40; // 对返回值进行赋值→对n赋值
cout<<n<<endl;
return 0;
}//程序输出40
常引用
不可通过常引用修改值(修改本值可以)
与引用属于不同类型
初始化:常引用=引用 or 普通变量(正确,反之则错)int n=100; const int & r=n; r=200; // error! n=200; // accepted
const 关键字(避免误改)
基本内容
常量
常量指针
不可通过常量指针更改值
可以改变常量指针指向
int r=4,rr=8; const int * p = &r; *p=6; // error! r=4; // ok p=&rr // ok可把非常量指针赋给常量指针(反之不行,因为常量指针能保证不修改非常量指针指向的那个值,反之不行,除非强制类型转换)
const int *p1; int *p2; p1=p2; // ok p2=p1; // error! // p2 现在允许修改原本被限制的内容,这可能导致通过 p1 指向的 const 对象被意外修改,违反 const 语义 p2=(int*)p1 // ok // 去掉了 const 限定,原本const的对象可以被p2修改,但修改的合法性取决于原对象是否真的是常量: // - 原对象本是非常量 → 安全 // - 原对象本是常量 → 未定义行为 (正常语法下,普通指针绝不允许绑定常量)
使用常量指针、常量引用、常量对象避免修改
// 常量指针 void PrintMy(const char*p){ strcpy(p,"this"); // error! cout<<p; //ok }// 常量引用 int n; const int & r=n; r=5; // error! n=3; // ok常量对象只能使用构造函数、析构函数和带const关键字的函数
// 常量对象 class Sample(){ private: int value; public: void GetValue(){} }; const Sample obj; //常量对象 obj.GetValue(); // error!
常量方法(常量成员函数)
即在类的成员函数说明后面加 const 关键字
- 不能改非静态属性的值(可以访问非静态,常量本就不能改,静态变量可以修改)
- 不能调用同类的非常量成员函数(静态成员除外)
- 定义和声明时都应使用 const 关键字
class Sample {
public:
int value;
void GetValue() const; // 常量成员函数
void func() { } // 非静态、非const成员函数
};
void Sample::GetValue() const {
// value = 0; // 错误:常量成员函数不能修改成员变量
// func(); // 错误:常量成员函数不能调用非const成员函数
}
int main() {
const Sample o; // 定义常量对象,使用默认构造函数
o.GetValue(); // 常量对象只能调用常量成员函数
return 0;
}
class Foo {
public:
int data = 42;
int get_val() const { // ✅ 按值返回,没问题
return data;
}
const int& get_ref() const { // ✅ const 引用,没问题
return data;
}
// int& get_ref() const { // ❌ 编译错误!
// return data; // 在 const 函数中 data 被视为 const int
// } // const int 不能绑定到 int&
};
常量成员函数的重载
两个函数名字、参数表均相同。但一者带有 const 关键字,另一者无,算重载
编译器将自动选择合适的
mutable 关键字
可以在 const 成员函数中修改 带mutable关键字的成员变量
class CTest {
private:
mutable int m_n1; // 使用mutable关键字
bool m_b2;
public:
bool GetData() const
{
m_n1++;
return m_b2;
}
};
加快速度与避免改变
为了防止生成形参导致的复制会引发复制构造函数的调用,开销较大
可以考虑将
T→T&→const T&(const 确保实参不改变)
以下是一段有关 const 函数调用说明
class Complex{ private: double real,imag; mutable int access; // 用于打破 const public: void setreal(double r){ real=r; } double getreal() const{ access++; // ok for "mutable" //imag++; // error! } } void Func(const Complex &c){ //c.real=1.0; // error! //c.setreal(20.0); //error! double val=c.getreal(); // ok // const 函数内部只能调用 const 的成员函数 }比较奇葩的改变方式:该变全局c来促使const的东西也变了
#include <iostream> class Complex { private: double real; double imag; public: // Constructor (构造函数) Complex(double r = 0, double i = 0) : real(r), imag(i) {} // Non-const member function (非常成员函数) // This can modify private members void setReal(double r) { real = r; } // Const member function (常成员函数) // This allows read-only access for the print statement void print() const { std::cout << "(" << real << ", " << imag << ")" << std::endl; } }; // Global Instance (全局变量) Complex global_obj(1.0, 1.0); // Function using a const reference void Function(const Complex & c) { std::cout << "Value of c before global change: "; c.print(); // Aliasing // Even though 'c' is const, the memory it points to is NOT immutable // if there is another non-const pointer/reference to it. global_obj.setReal(100.0); std::cout << "Value of c after global change: "; c.print(); } int main() { // Passing the global object to the function // Inside Function(), 'c' will be an alias for 'global_obj' Function(global_obj); return 0; }
动态内存管理
谁new谁到结尾delete,一一对应,不可缺少(有new才delete)
new 关键字
变量:
p= new T数组:
p=new T[N]
其中 T 是任意类型名,N可以是整型表达式
注意⚠️:分配后调用时编译器不会检查是否越界,应自我约束
*注意⚠️:一定要用 delete 释放内存!且 delete 必须指向new出来的所有空间*
int *p=new int;
int *pp=new int[20];
*p=5;
delete p;
p[0]=1;
// delete pp; // 不报错,但是只是释放了第一个元素!
delete []pp; // 正确!
int rows = 3, cols = 4; // 分配 int** p = new int*[rows]; // 外层:指针数组 for (int i = 0; i < rows; ++i) { p[i] = new int[cols]; // 内层:每行一个 int 数组 } // 释放(顺序必须反过来) for (int i = 0; i < rows; ++i) { delete[] p[i]; // 先释放每一行的 int[] } delete[] p; // 最后释放外层的 int*[]int* inner = new int(10); int** p = &inner; // 这没有动态分配二级内存,只需要: delete inner; // 或 delete *p; // p 本身是栈上的,不需要 delete
函数重载
函数重载,即多个函数名字相同,然而参数个数或参数类型不相同
注意⚠️:如果出现歧义,则编译报错!
int max(int a, int b) {return (a > b) ? a : b;}
double max(double a, double b) {return (a > b) ? a : b;}
int max(int a, int b, int c) {return max(max(a, b), c);}
int main() {
cout<<max(3, 5)<<endl; // 调用 A
cout<<max(3.7, 2.9)<<endl; // 调用 B
cout<<max(3, 5, 7)<<endl; // 调用 C
cout << max(3, 5.3) << endl;// error! 二义性报错!
return 0;
}
函数的缺省值
定义函数的时候可以让最右边的连续若干参数有缺省值,调用函数的时候,若不写参数则默认为缺省值
- 提高可扩展性
- 方便添加新参数与修改
注意⚠️:只能最右边开始

类的定义和使用
定义类
class 类名
{
访问范围说明符:
// 可选private,public,protected,可出现多次
// private:指定私有成员,只有该类的成员函数可以访问
// public: 任何地方皆可访问
// protected:参见“继承”章节
成员变量 1
成员变量 2
...
成员函数声明 1
声明函数声明 2
函数(){}
// 函数类内声明+类外定义 或者 类内直接定义(此时默认为内联函数)
访问范围说明符:
更多成员变量
更多成员函数声明
...
// class在缺省情况下访问范围为 private ,struct 在默认情况下访问范围为 public
}; // ⚠️class 定义必须以英文分号 ; 结尾
返回值类型 类名::函数名()
{
语句组
}
注意⚠️:类定义以分号结尾
注意⚠️:
A::说明后面的函数是类A的成员函数,而非普通函数一个类的成员函数可以互相调用
类的成员函数也可以重载,可以设定参数默认值(任何有定义的表达式皆可,如:
int Max( int m, int n); int a, b; void Function2 ( int x,int y= Max(a, b), int z=a*b) {...})
没写private,一定一定要写上!!!
定义对象
// 定义对象
类名 对象名;
对象占用的空间
一般来说,一个对象占用的内存空间大小 = 其成员变量的体积之和,可采用
sizeof(类名)查看Data Type Memory Size (Bytes) Typical Range / Note char1 Stores a single character (单个字符) bool1 Represents trueorfalseint4 Standard integer (标准整数) long4 or 8 Depends on the system (取决于系统架构) long long8 Large integer (大整数) double8 Double-precision floating point (双精度浮点数) float4 单精度浮点数 指针 4 or 8 固定不变(32位系统为4,64位系统为8),现在大部分为8 每个对象各有自己的一份存储空间,互不影响
成员函数内存只有一份,但可以作用于不同函数
访问对象的成员
class CRectangle
{
public:
int w,h;
void init(int ww,int hh)
{
w=ww;
hh=h;
}
};
CRectangle r1,r2;
// 方法一:对象名.成员名
r1.w=5;
r1.init(3,4);
// 方法二:指针->成员名
CRectangle *p1 = &r1;
CRectangle *p2 = &r2;
p1->w=6;
p2->init(3,4)
// 方法三:引用名.成员名
CRectangle &rr = r2;
rr.w =5;
rr.init(5,4); // rr的值变了,r2的值也变了
类成员的访问权限(private 隐藏机制)
class 缺省情况下为 private ,struct 默认为 public
注意⚠️:
类成员函数内部能够访问 :
- 当前对象的全部属性(变量和函数)
- ⚠️同类其他对象的全部属性
类成员函数以外的地方,只能够访问该类对象的公有成员,也不能cout私有成员(类似于被屏蔽的感觉)
设置私有成员的好处:
- 若未来成员变量的类型等属性修改后,只需要更改成员函数即可,否则得把所有直接访问成员变量的语句修改
- 避免对对象的不正确操作
内联成员函数
在每个调用点直接插入函数,增大函数体的体积,但是减少函数调用开销,加快程序速度
// 以下两种方法等价
class A
{
//内写内联
void func1(){};
//声明内联
inline void func2 ();
}
void A::func2 (){};

inline 对于编译器只是建议而非命令 编译器可能忽略内联请求(对于过长的函数) 也可能自动内联未标记为 inline 的短小函数(即使在类外部定义)