これが私の意味です:
class MyClass {
int arr1[100];
int arr2[100];
int len = 100;
void add(int* x1, int* x2, int size) {
for (int i = 0; i < size; i++) {
x1[i] += x2[i];
}
}
};
int main() {
MyClass myInstance;
// Fill the arrays...
myInstance.add(myInstance.arr1, myInstance.arr2, myInstance.len);
}
add
はクラスメソッドであるため、必要なすべての変数にすでにアクセスできますが、これは悪い考えですか?これを行うべきか、すべきでない理由はありますか?
クラスには違う方法でやることがあるかもしれませんが、直接的な質問に答えるために、私の答えは
はい、それは悪い考えです
これの主な理由は、add
関数に渡されるものを制御できないためです。確かにそれがメンバー配列の1つであることを期待していますが、誰かが100より小さいサイズの別の配列を渡す場合、または100を超える長さで渡す場合はどうなりますか?
バッファオーバーランの可能性が生じたことが起こります。そして、それは至る所で悪いことです。
さらに(私にとって)明白な質問に答えるには:
Cスタイルの配列とC++を混在させています。私はC++の第一人者ではありませんが、C++には配列を処理するより優れた(より安全な)方法があることを知っています
クラスにすでにメンバー変数がある場合、なぜそれらを渡す必要があるのですか?これは、建築上の問題です。
C++の経験が豊富な他の人(私は10年または15年前にその使用をやめました)は、問題を説明する説得力のある方法を持っている可能性があり、おそらくより多くの問題も思い付くでしょう。
一部のクラス変数を使用してクラスメソッドを呼び出すことは、必ずしも悪いことではありません。しかし、クラスの外側からするのは非常に悪い考えであり、OOデザインの基本的な欠陥、つまり適切な encapsulation の欠如:
len
が配列の長さであることを認識し、一貫して使用する必要があります。これは、最小の知識の 原則 に反します。このようなクラスの内部の詳細への依存は、非常にエラーが発生しやすく、リスクを伴います。len
をより新しい_std::vector<int>
_に変更したい場合)。これも変更する必要があるためです。クラスを使用するすべてのコード。MyClass
オブジェクトに大混乱をもたらす可能性があります(いわゆる class invariants )コードをリファクタリングする必要があります。
private
またはprotected
にします。ただし、それを行わない正当な理由がない限り。object.action(additional parameters for the action)
)。この設計の意図が、このクラスインスタンスから来ていないデータに対してこのメソッドを再利用できるようにすることである場合、それを static
method として宣言することができます。 =。
静的メソッドは、クラスの特定のインスタンスに属していません。それはむしろクラス自体のメソッドです。静的メソッドはクラスの特定のインスタンスのコンテキストでは実行されないため、静的として宣言されているメンバー変数にのみアクセスできます。メソッドadd
がMyClass
で宣言されているメンバー変数のいずれも参照していないことを考えると、静的として宣言されることをお勧めします。
ただし、他の回答で述べられている安全上の問題は有効です。両方の配列が少なくともlen
であるかどうかをチェックしていません。モダンで堅牢なC++を作成する場合は、配列の使用を避けてください。クラス std::vector
可能な限り代わりに。通常の配列とは異なり、ベクトルは自身のサイズを知っています。したがって、自分でサイズを追跡する必要はありません。それらのメソッドのほとんど(すべてではありません!)も自動境界チェックを実行します。これにより、最後を超えて読み書きしたときに例外が発生することが保証されます。通常の配列アクセスは境界チェックを行わないため、せいぜいsegfaultが発生し、 悪用可能なバッファオーバーフローの脆弱性 を引き起こす可能性があります。
クラスのメソッドは、クラス内のカプセル化されたデータを理想的に操作します。あなたの例では、addメソッドにパラメータがある理由はなく、単にクラスのプロパティを使用します。
オブジェクト(の一部)をメンバー関数に渡すことは問題ありません。関数を実装するとき、あなたは常にエイリアスの可能性に注意してくださいとその結果をしなければなりません。
もちろん、コントラクトは、言語がそれをサポートしていても、ある種のエイリアスを未定義のままにするかもしれません。
顕著な例:
v.Push_back(v[2]);
のようなもの。std::copy()
は、宛先がソース内で開始しないことを前提としています。しかし、あなたの例には異なる問題があります:
this
も参照しません。それは単に間違った場所にある無料のユーティリティ関数のように振る舞います。要約するとはい、この例は良くありません。ただし、メンバー関数がメンバーオブジェクトを渡されるためではありません。
特にこれを行うことはいくつかの理由から悪い考えですが、それらすべてがあなたの質問に不可欠であるとは思えません:
vector<int>
はあなたの人生を楽にします。これは、C++に対する古典的な「C with classes」アプローチです。実際には、これは熟練したC++プログラマーが書くものではありません。 1つは、コンテナーライブラリを実装している場合を除いて、生のC配列を使用することは、一般的にはお勧めできません。
このようなものがより適切でしょう:
// Don't forget to compile with -std=c++17
#include <iostream>
using std::cout; // This style is less common, but I prefer it
using std::endl; // I'm sure it'll incite some strongly opinionated comments
#include <array>
using std::array;
#include <algorithm>
#include <vector>
using std::vector;
class MyClass {
public: // Begin lazy for brevity; don't do this
std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
std::array<int, 5> arr2 = {10, 10, 10, 10, 10};
};
void elementwise_add_assign(
std::array<int, 5>& lhs,
const std::array<int, 5>& rhs
) {
std::transform(
lhs.begin(), lhs.end(), rhs.begin(),
lhs.begin(),
[](auto& l, const auto& r) -> int {
return l + r;
});
}
int main() {
MyClass foo{};
elementwise_add_assign(foo.arr1, foo.arr2);
for(auto const& value: foo.arr1) {
cout << value << endl; // arr1 values have been added to
}
for(auto const& value: foo.arr2) {
cout << value << endl; // arr2 values remain unaltered
}
}