本文已經收錄到Github倉庫,該倉庫包含電腦基礎、Java基礎、多線程、JVM、資料庫、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分散式、微服務、設計模式、架構、校招社招分享等核心知識點,歡迎star~ Github地址:https://github.c ...
class and struct
目錄
前文
c++ 中的class 和struct
struct 預設為 public
class 預設為private
問題
左值引用,右值引用區別?
什麼時候需要定值淺拷貝和深拷貝的函數。
傳值和傳值的區別
對象與引用
引用的傳遞
返回引用就意味著是data[ind]的別名。
using namespace std;
class Vector{
public:
Vector(int n =100) : n(n),data(new int[n]){}
int &at(int ind){return data[ind];}
private :
int n ;
int *data;
};
int main(){
Vector arr;
for(int i=0; i< 1; i++){
arr.at(i) = i;
}
return 0;
}
對象 copy
實際上兩種copy是邏輯上的
class Vector{
public:
Vector(int n =100) : n(n),data(new int[n]){}
int &at(int ind){
cout << "ind" << ind << endl;
return data[ind];
}
int &operator[](int ind) {return data[ind];}
void output (int m = -1){
if(m == -1) m = n;
cout <<"Arr" << this << endl;
for(int i = 0; i < m; i++){
cout << data[i] << " ";
}
cout << endl;
return ;
}
private :
int n ;
int *data;
};
shallow copy
int main(){
Vector arr;
for(int i=0; i< 10; i++) arr[i]=i;
arr.output(10);
Vector arr2(arr);
arr2.output(10);
arr2[3]=1000;
arr.output(10);
arr2.output(10);
return 0;
}
Arr0x7ffcddc92630 arr
0 1 2 3 4 5 6 7 8 9
Arr0x7ffcddc92620 arr 2
0 1 2 3 4 5 6 7 8 9
Arr0x7ffcddc92630
0 1 2 1000 4 5 6 7 8 9
Arr0x7ffcddc92620
0 1 2 1000 4 5 6 7 8 9
arr {n=100 data=0x000002196c4ad400 {0} } Vector
arr2 {n=100 data=0x000002196c4ad400 {0} } Vector
vs 的debug 模式下 data
本代碼將arr 拷貝給了arr2 ,本身屬於兩個不同的對象。
既然屬於不同的對象,arr2的變動不應對arr1發生改變,但是事與願違 ,因為受拷貝行為的影響,依次性的拷貝,由於是指針域,拷貝了同一個存儲區的值。所以 arr2 發生更改,arr1 也會發生更改。
☕如果copy 的對象是指針類型時,這個方式就無法適用了.
depthcopy
指針類型並非是賦值關係,存arr2 應該是一個額外的存儲區。
memcpy(data,a.data,sizeof(T) *n);
當前方法在一定的簡單類型的場景下可以進行深拷貝,如果類型中存在了指針類型則也會出現兩個指針指向同一個地址的的情況。
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define BEGINS(x) namespace x {//begin of namespace
#define ENDS(x)} //end of namespace x
BEGINS(xxx)
class A {
public :
int x , y;
};
ostream &operator<<(ostream &out ,const A &a){
out << "(" << a.x << " , " << a.y << ")";
return out;
}
template<typename T>
class Vector{
public:
Vector(int n =100) : n(n),data(new T[n]){}
Vector (const Vector &a): n(a.n){
// 實現深拷貝
data = new T[n];
/*
for(int i = 0; i < n; i++){
data[i]=a.data[i];
} */
//實現以上的效果
//1 : memcpy 可能是複製類型是複雜類型的對象
memcpy(data,a.data,sizeof(T) *n);
return ;
}
T &at(int ind){
cout << "ind" << ind << endl;
return data[ind];
}
T &operator[](int ind) {return data[ind];}
void output (int m = -1){
if(m == -1) m = n;
cout << "Arr size " << sizeof(data) << " " << this << endl;
for(int i = 0; i < m; i++){
cout << data[i] << " ";
}
cout << endl;
return ;
}
private :
int n ;
T *data;
};
ENDS(xxx)
簡單類型
BEGINS(test1)
using namespace xxx;
int main(){
Vector<int> arr;
for(int i=0; i< 10; i++) arr[i]=i;
arr.output(10);
Vector<int> arr2(arr);
arr2.output(10);
arr2[3]=1000;
arr.output(10);
arr2.output(10);
return 0;
}
ENDS(test1)
Arr size 8 0x7ffc86e546d0
0 1 2 3 4 5 6 7 8 9
Arr size 8 0x7ffc86e546c0
0 1 2 3 4 5 6 7 8 9
Arr size 8 0x7ffc86e546d0
0 1 2 3 4 5 6 7 8 9
Arr size 8 0x7ffc86e546c0
0 1 2 1000 4 5 6 7 8 9
☕由結果可以看到,很顯然在copy後並沒有影響arr1的指針域
複雜類型
BEGINS(test2)
using namespace xxx;
int main(){
Vector<A> arr1;
for(int i = 0; i < 10; i++){
arr1[i].x = i;
arr1[i].y = 2 * i;
}
arr1.output(10);
Vector<A> arr2(arr1);
arr2[3] = (A){4, 100};
arr2.output(10);
arr1.output(10);
return 0;
}
ENDS(test2)
Arr size 8 0x7ffc053fdeb0 //原
(0 , 0) (1 , 2) (2 , 4) (3 , 6) (4 , 8) (5 , 10) (6 , 12) (7 , 14) (8 , 16) (9 , 18)
Arr size 8 0x7ffc053fdea0
(0 , 0) (1 , 2) (2 , 4) (4 , 100) (4 , 8) (5 , 10) (6 , 12) (7 , 14) (8 , 16) (9 , 18)
Arr size 8 0x7ffc053fdeb0
(0 , 0) (1 , 2) (2 , 4) (3 , 6) (4 , 8) (5 , 10) (6 , 12) (7 , 14) (8 , 16) (9 , 18)
☕在不涉及對象中的(指針類型)(其中類型不需要深拷貝的場景)的情況下,可以實現深拷貝。
指針類型的拷貝
memcpy 是無法做到深拷貝的,無法完美的遷移。
BEGINS(test3)
using namespace xxx;
int main(){
Vector<Vector<int>> arr1;
Vector<Vector<int>> arr2(arr1);
arr2[2][2]=1000;
for(int i =0; i <3 ; i++){
arr1[i].output(3);
cout <<endl;
}
for(int i =0; i <3 ; i++){
arr2[i].output(3);
cout <<endl;
}
return 0;
}
ENDS(test3)
Arr size 8 0xbe4680
0 0 0
Arr size 8 0xbe4690
0 0 1000
Arr size 8 0xbeef40
0 0 0
Arr size 8 0xbeef50
0 0 0
Arr size 8 0xbeef60
0 0 1000
原位構造
位構造 new (地址),其意思為:在某個地址上調用某個構造函數,構造一個相關的對象
如果數據類型T執行深拷貝的函數,將call T類型的拷貝構造函數
https://hedzr.com/c++/variant/in-place-construction-in-cxx/ 參考文章
Vector(const Vector &a):n(a.n){
// data = new T[n];//實現深拷貝, 帶new 就調用n次構造函數
data =(T *)malloc(sizeof(T) * n);
/* for(int i = 0; i < n; i++){
data[i]=a.data[i];
} */
//1 : memcpy 可能是複製類型是複雜類型的對象
//原位構造 new (地址),其意思為:在某個地址上調用某個構造函數,構造一個相關的對象
for(int i = 0; i < n; i++){
//如果數據類型T執行深拷貝的函數,將call T類型的拷貝構造函數
new (data + i) T(a.data[i]); //這裡也調了i次
}
// memcpy(data,a.data,sizeof(T) * n);
return ;
}
返回值優化 (RVO)
對象: 已經經歷了構造過程的數據區 。obj (申請對象數據區)→匹配構造函數 →完成構造(某對象)
-fno-elide-constructors 關閉返回值優化
拷貝構造的現象
按照流程應該是發生了三次構造 ? 為什麼輸出的解構為什麼是同一個地址
按照正常邏輯, 中間的匿名對象應該沒意義的 只是一個中轉的功能
所以 第一步就吧匿名的臨時變數優化掉
第二 ->tmep 拷貝給A對象 ,既然是拷貝他們是一模一樣,加在temp對象所有的操作都會在去往A上
temp 對象其實可以看做A對象的別名,這就是返回值優化的最終版本
最終是 吧temp 當做A對象的引用了
#include<iostream>
#include<queue>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<algorithm>
using namespace std;
class A{
public:
A(){
cout << this <<" default constructor" << endl;
}
A(int x){
cout << this << " param cpms" << endl;
}
A(const A&a){
cout << " copy constructor" << endl;
}
};
A func(){
A temp(3); // 代參數的構造
cout << " temp " << &temp << endl;
return temp;// 這裡應該是屬於匿名變數拷貝給了 A
}
int main(){
A a =func(); //匿名對象在拷貝A
cout <<" a :" << &a <<endl;
return 0;
}
關閉的優化後
0x7ffc7872633f param cpms
temp 0x7ffc7872633f
copy constructor
copy constructor
a :0x7ffc7872635e
優化後
0x7ffef5b875cf param cpms
temp 0x7ffef5b875cf
a :0x7ffef5b875cf
關鍵字
隱形的行為,顯性化 ,將代碼的層面的規則,變成顯示的規則
default
不一樣點 :如果想要實現拷貝構造必須是初始化列表挨個拷貝, default 關鍵字會挨個執行拷貝方法
如果是預設的構造函數,他是一個東西
class A{
A() = default; // 隱性的行為顯性化
A(const A &) =default;
A(const A &) {} //這和上面不是一個東西
};
delete
刪除後,如果想使用,編譯會直接報錯。
class A{ |~
A() = default; // 隱性的行為顯性化 |~
//A(const A &) =default; |~
A(const A &) = delete; |~
|~
// A() {} 這和上面不是一個東西 |~
}; |~
函數重載
如果一個作用內,函數名稱相同,但是參數列表不同,稱函數重載 (參數類型不同,參數個數不同(記得預設參數的問題))。
強調一下 和返回值沒關係
問題
函數重載 好處?
- 有了函數重載,可以吧把一類通過參數區分的功能,命名成一個函數名。(精細化的處理流程)
- 通過函數參數的列表對函數進行提示
- 對函數進行擴展
code
int func(int x ){
return 2 * x;
}
double func(double x){
return 2.0 * x;
}
int ABS(int x ){
return abs(x);
}
double ABS(double x){
return fabs(x);
}
int main(){
cout << func(1) << endl;
cout << func(22.0) << endl;
cout << ABS(222.56) << endl;
cout << ABS(-233) << endl ;
return 0;
}